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