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