1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2021 FUJITSU LIMITED. All rights reserved
4 * Author: Yang Xu <[email protected]>
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
46 static int32_t fmt_id = QFMT_VFS_V1;
47 static int test_id, mount_flag;
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, 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
setup(void)154 static 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
cleanup(void)171 static 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
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(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
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 .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