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