1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2019-2021 FUJITSU LIMITED. All rights reserved. 4 * Author: Yang Xu <xuyang2018.jy@fujitsu.com> 5 */ 6 7/*\ 8 * [Description] 9 * 10 * This testcase checks that quotactl(2) on ext4 filesystem succeeds to: 11 * 12 * - turn on quota with Q_QUOTAON flag for project 13 * - set disk quota limits with Q_SETQUOTA flag for project 14 * - get disk quota limits with Q_GETQUOTA flag for project 15 * - set information about quotafile with Q_SETINFO flag for project 16 * - get information about quotafile with Q_GETINFO flag for project 17 * - get quota format with Q_GETFMT flag for project 18 * - get disk quota limit greater than or equal to ID with Q_GETNEXTQUOTA flag for project 19 * - turn off quota with Q_QUOTAOFF flag for project 20 * 21 * Minimum e2fsprogs version required is 1.43. 22 */ 23 24#include <errno.h> 25#include <string.h> 26#include <unistd.h> 27#include <sys/stat.h> 28#include <sys/mount.h> 29#include "tst_test.h" 30#include "quotactl_syscall_var.h" 31 32#define FMTID QFMT_VFS_V1 33 34static int32_t fmt_id = FMTID; 35static int test_id, mount_flag; 36static struct dqblk set_dq = { 37 .dqb_bsoftlimit = 100, 38 .dqb_valid = QIF_BLIMITS 39}; 40static struct dqblk res_dq; 41static struct dqinfo set_qf = { 42 .dqi_bgrace = 80, 43 .dqi_valid = IIF_BGRACE 44}; 45 46static struct dqinfo res_qf; 47static int32_t fmt_buf; 48 49static struct if_nextdqblk res_ndq; 50static int getnextquota_nsup; 51 52static struct tcase { 53 int cmd; 54 int *id; 55 void *addr; 56 void *set_data; 57 void *res_data; 58 int sz; 59 char *des; 60 char *tname; 61} tcases[] = { 62 {QCMD(Q_QUOTAON, PRJQUOTA), &fmt_id, NULL, 63 NULL, NULL, 0, "turn on quota for project", 64 "QCMD(Q_QUOTAON, PRJQUOTA)"}, 65 66 {QCMD(Q_SETQUOTA, PRJQUOTA), &test_id, &set_dq, 67 NULL, NULL, 0, "set disk quota limit for project", 68 "QCMD(Q_SETQUOTA, PRJQUOTA)"}, 69 70 {QCMD(Q_GETQUOTA, PRJQUOTA), &test_id, &res_dq, 71 &set_dq.dqb_bsoftlimit, &res_dq.dqb_bsoftlimit, 72 sizeof(res_dq.dqb_bsoftlimit), "get disk quota limit for project", 73 "QCMD(Q_GETQUOTA, PRJQUOTA)"}, 74 75 {QCMD(Q_SETINFO, PRJQUOTA), &test_id, &set_qf, 76 NULL, NULL, 0, "set information about quotafile for project", 77 "QCMD(Q_SETINFO, PRJQUOTA"}, 78 79 {QCMD(Q_GETINFO, PRJQUOTA), &test_id, &res_qf, 80 &set_qf.dqi_bgrace, &res_qf.dqi_bgrace, sizeof(res_qf.dqi_bgrace), 81 "get information about quotafile for project", 82 "QCMD(Q_GETINFO, PRJQUOTA"}, 83 84 {QCMD(Q_GETFMT, PRJQUOTA), &test_id, &fmt_buf, 85 &fmt_id, &fmt_buf, sizeof(fmt_buf), 86 "get quota format for project", "QCMD(Q_GETFMT, PRJQUOTA)"}, 87 88 {QCMD(Q_GETNEXTQUOTA, PRJQUOTA), &test_id, &res_ndq, 89 &test_id, &res_ndq.dqb_id, sizeof(res_ndq.dqb_id), 90 "get next disk quota limit for project", 91 "QCMD(Q_GETNEXTQUOTA, PRJQUOTA)"}, 92 93 {QCMD(Q_QUOTAOFF, PRJQUOTA), &test_id, NULL, 94 NULL, NULL, 0, "turn off quota for project", 95 "QCMD(Q_QUOTAOFF, PRJQUOTA)"}, 96 97}; 98 99static void setup(void) 100{ 101 const char *const fs_opts[] = {"-I 256", "-O quota,project", NULL}; 102 103 quotactl_info(); 104 SAFE_MKFS(tst_device->dev, tst_device->fs_type, fs_opts, NULL); 105 SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL); 106 mount_flag = 1; 107 fd = SAFE_OPEN(MNTPOINT, O_RDONLY); 108 109 TEST(do_quotactl(fd, QCMD(Q_GETNEXTQUOTA, PRJQUOTA), tst_device->dev, 110 test_id, (void *) &res_ndq)); 111 if (TST_ERR == EINVAL || TST_ERR == ENOSYS) 112 getnextquota_nsup = 1; 113} 114 115static void cleanup(void) 116{ 117 if (fd > -1) 118 SAFE_CLOSE(fd); 119 if (mount_flag && tst_umount(MNTPOINT)) 120 tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT); 121} 122 123static void verify_quota(unsigned int n) 124{ 125 struct tcase *tc = &tcases[n]; 126 127 res_dq.dqb_bsoftlimit = 0; 128 res_qf.dqi_igrace = 0; 129 fmt_buf = 0; 130 131 tst_res(TINFO, "Test #%d: %s", n, tc->tname); 132 133 if (tc->cmd == QCMD(Q_GETNEXTQUOTA, PRJQUOTA) && getnextquota_nsup) { 134 tst_res(TCONF, "current system doesn't support this cmd"); 135 return; 136 } 137 138 TST_EXP_PASS_SILENT(do_quotactl(fd, tc->cmd, tst_device->dev, *tc->id, tc->addr), 139 "do_quotactl to %s", tc->des); 140 if (!TST_PASS) 141 return; 142 143 if (memcmp(tc->res_data, tc->set_data, tc->sz)) { 144 tst_res(TFAIL, "quotactl failed to %s", tc->des); 145 tst_res_hexd(TINFO, tc->res_data, tc->sz, "retval: "); 146 tst_res_hexd(TINFO, tc->set_data, tc->sz, "expected: "); 147 return; 148 } 149 150 tst_res(TPASS, "quotactl succeeded to %s", tc->des); 151} 152 153static struct tst_test test = { 154 .needs_root = 1, 155 .needs_drivers = (const char *const []) { 156 "quota_v2", 157 NULL 158 }, 159 .min_kver = "4.5", /* commit 689c958cbe6b (ext4: add project quota support) */ 160 .test = verify_quota, 161 .tcnt = ARRAY_SIZE(tcases), 162 .setup = setup, 163 .cleanup = cleanup, 164 .needs_device = 1, 165 .dev_fs_type = "ext4", 166 .mntpoint = MNTPOINT, 167 .test_variants = QUOTACTL_SYSCALL_VARIANTS, 168 .needs_cmds = (const char *[]) { 169 "mkfs.ext4 >= 1.43.0", 170 NULL 171 } 172}; 173