1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) Crackerjack Project., 2007 4 * Copyright (c) 2016-2021 FUJITSU LIMITED. All rights reserved 5 * Author: Xiao Yang <yangx.jy@cn.fujitsu.com> 6 * Author: Yang Xu <xuyang2018.jy@fujitsu.com> 7 */ 8 9/*\ 10 * [Description] 11 * This testcases checks that quotactl(2) on ext4 filesystem succeeds to: 12 * 13 * - turn on quota with Q_QUOTAON flag for user 14 * - set disk quota limits with Q_SETQUOTA flag for user 15 * - get disk quota limits with Q_GETQUOTA flag for user 16 * - set information about quotafile with Q_SETINFO flag for user 17 * - get information about quotafile with Q_GETINFO flag for user 18 * - get quota format with Q_GETFMT flag for user 19 * - update quota usages with Q_SYNC flag for user 20 * - get disk quota limit greater than or equal to ID with Q_GETNEXTQUOTA flag for user 21 * - turn off quota with Q_QUOTAOFF flag for user 22 * - turn on quota with Q_QUOTAON flag for group 23 * - set disk quota limits with Q_SETQUOTA flag for group 24 * - get disk quota limits with Q_GETQUOTA flag for group 25 * - set information about quotafile with Q_SETINFO flag for group 26 * - get information about quotafile with Q_GETINFO flag for group 27 * - get quota format with Q_GETFMT flag for group 28 * - update quota usages with Q_SYNC flag for group 29 * - get disk quota limit greater than or equal to ID with Q_GETNEXTQUOTA flag for group 30 * - turn off quota with Q_QUOTAOFF flag for group 31 */ 32 33#include <errno.h> 34#include <string.h> 35#include <unistd.h> 36#include <stdio.h> 37#include "tst_test.h" 38#include "quotactl_fmt_var.h" 39 40#define USRPATH MNTPOINT "/aquota.user" 41#define GRPPATH MNTPOINT "/aquota.group" 42#define MNTPOINT "mntpoint" 43 44static int32_t fmt_id; 45static int test_id; 46static char usrpath[] = USRPATH; 47static char grppath[] = GRPPATH; 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, usrpath, 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, usrpath, 111 NULL, NULL, 0, "turn off quota for user", 112 "QCMD(Q_QUOTAOFF, USRQUOTA)"}, 113 114 {QCMD(Q_QUOTAON, GRPQUOTA), &fmt_id, grppath, 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, grppath, 150 NULL, NULL, 0, "turn off quota for group", 151 "QCMD(Q_QUOTAOFF, GRPQUOTA)"}, 152}; 153 154static void setup(void) 155{ 156 const struct quotactl_fmt_variant *var = &fmt_variants[tst_variant]; 157 const char *const cmd[] = {"quotacheck", "-ugF", var->fmt_name, MNTPOINT, NULL}; 158 159 tst_res(TINFO, "quotactl() with %s format", var->fmt_name); 160 SAFE_CMD(cmd, NULL, NULL); 161 fmt_id = var->fmt_id; 162 163 SAFE_ACCESS(USRPATH, F_OK); 164 165 SAFE_ACCESS(GRPPATH, F_OK); 166 167 TEST(quotactl(QCMD(Q_GETNEXTQUOTA, USRQUOTA), tst_device->dev, 168 test_id, (void *) &res_ndq)); 169 if (TST_ERR == EINVAL || TST_ERR == ENOSYS) 170 getnextquota_nsup = 1; 171} 172 173static void cleanup(void) 174{ 175 SAFE_UNLINK(USRPATH); 176 SAFE_UNLINK(GRPPATH); 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(quotactl(tc->cmd, tst_device->dev, *tc->id, tc->addr), 196 "quotactl to %s", tc->des); 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 .mount_device = 1, 219 .dev_fs_type = "ext4", 220 .mntpoint = MNTPOINT, 221 .mnt_data = "usrquota,grpquota", 222 .needs_cmds = (const char *const []) { 223 "quotacheck", 224 NULL 225 }, 226 .setup = setup, 227 .cleanup = cleanup, 228 .test_variants = QUOTACTL_FMT_VARIANTS, 229}; 230