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
44 static int32_t fmt_id;
45 static int test_id;
46 static char usrpath[] = USRPATH;
47 static char grppath[] = GRPPATH;
48 static struct dqblk set_dq = {
49 .dqb_bsoftlimit = 100,
50 .dqb_valid = QIF_BLIMITS
51 };
52 static struct dqblk res_dq;
53
54 static struct dqinfo set_qf = {
55 .dqi_bgrace = 80,
56 .dqi_valid = IIF_BGRACE
57 };
58 static struct dqinfo res_qf;
59 static int32_t fmt_buf;
60 static int getnextquota_nsup;
61
62 static struct if_nextdqblk res_ndq;
63
64 static 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
setup(void)154 static 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
cleanup(void)173 static void cleanup(void)
174 {
175 SAFE_UNLINK(USRPATH);
176 SAFE_UNLINK(GRPPATH);
177 }
178
verify_quota(unsigned int n)179 static 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
210 static 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