1 /*
2  * Copyright (c) 2013 Fujitsu Ltd.
3  * Author: DAN LI <li.dan@cn.fujitsu.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  */
17 
18 /*
19  * Test Name: quotactl02
20  *
21  * Description:
22  * This testcase checks basic flags of quotactl(2) for an XFS file system:
23  * 1) quotactl(2) succeeds to turn off xfs quota and get xfs quota off status.
24  * 2) quotactl(2) succeeds to turn on xfs quota and get xfs quota on status.
25  * 3) quotactl(2) succeeds to set and use Q_XGETQUOTA to get xfs disk quota
26  *    limits.
27  * 4) quotactl(2) succeeds to set and use Q_XGETNEXTQUOTA to get xfs disk
28  *    quota limits.
29  */
30 #define _GNU_SOURCE
31 #include <errno.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <sys/quota.h>
35 #include "config.h"
36 
37 #if defined(HAVE_QUOTAV2) || defined(HAVE_QUOTAV1)
38 # include <sys/quota.h>
39 #endif
40 
41 #if defined(HAVE_XFS_QUOTA)
42 # include <xfs/xqm.h>
43 #endif
44 
45 #include "tst_test.h"
46 #include "lapi/quotactl.h"
47 
48 #if defined(HAVE_XFS_QUOTA) && (defined(HAVE_QUOTAV2) || defined(HAVE_QUOTAV1))
49 static void check_qoff(int, char *);
50 static void check_qon(int, char *);
51 static void check_qlim(int, char *);
52 
53 static uint32_t test_id;
54 static struct fs_disk_quota set_dquota = {
55 	.d_rtb_softlimit = 1000,
56 	.d_fieldmask = FS_DQ_RTBSOFT
57 };
58 static uint32_t qflag = XFS_QUOTA_UDQ_ENFD;
59 static const char mntpoint[] = "mnt_point";
60 
61 static struct t_case {
62 	int cmd;
63 	void *addr;
64 	void (*func_check)();
65 	int check_subcmd;
66 	char *des;
67 } tcases[] = {
68 	{QCMD(Q_XQUOTAOFF, USRQUOTA), &qflag, check_qoff, Q_XGETQSTAT,
69 	"turn off xfs quota and get xfs quota off status"},
70 	{QCMD(Q_XQUOTAON, USRQUOTA), &qflag, check_qon, Q_XGETQSTAT,
71 	"turn on xfs quota and get xfs quota on status"},
72 	{QCMD(Q_XSETQLIM, USRQUOTA), &set_dquota, check_qlim, Q_XGETQUOTA,
73 	"Q_XGETQUOTA"},
74 	{QCMD(Q_XSETQLIM, USRQUOTA), &set_dquota, check_qlim, Q_XGETNEXTQUOTA,
75 	"Q_XGETNEXTQUOTA"},
76 };
77 
check_qoff(int subcmd,char * desp)78 static void check_qoff(int subcmd, char *desp)
79 {
80 	int res;
81 	struct fs_quota_stat res_qstat;
82 
83 	res = quotactl(QCMD(subcmd, USRQUOTA), tst_device->dev,
84 	               test_id, (void*) &res_qstat);
85 	if (res == -1) {
86 		tst_res(TFAIL | TERRNO,
87 			"quotactl() failed to get xfs quota off status");
88 		return;
89 	}
90 
91 	if (res_qstat.qs_flags & XFS_QUOTA_UDQ_ENFD) {
92 		tst_res(TFAIL, "xfs quota enforcement was on unexpectedly");
93 		return;
94 	}
95 
96 	tst_res(TPASS, "quoactl() succeeded to %s", desp);
97 }
98 
check_qon(int subcmd,char * desp)99 static void check_qon(int subcmd, char *desp)
100 {
101 	int res;
102 	struct fs_quota_stat res_qstat;
103 
104 	res = quotactl(QCMD(subcmd, USRQUOTA), tst_device->dev,
105 	               test_id, (void*) &res_qstat);
106 	if (res == -1) {
107 		tst_res(TFAIL | TERRNO,
108 			"quotactl() failed to get xfs quota on status");
109 		return;
110 	}
111 
112 	if (!(res_qstat.qs_flags & XFS_QUOTA_UDQ_ENFD)) {
113 		tst_res(TFAIL, "xfs quota enforcement was off unexpectedly");
114 		return;
115 	}
116 
117 	tst_res(TPASS, "quoactl() succeeded to %s", desp);
118 }
119 
check_qlim(int subcmd,char * desp)120 static void check_qlim(int subcmd, char *desp)
121 {
122 	int res;
123 	static struct fs_disk_quota res_dquota;
124 
125 	res_dquota.d_rtb_softlimit = 0;
126 
127 	res = quotactl(QCMD(subcmd, USRQUOTA), tst_device->dev,
128 	               test_id, (void*) &res_dquota);
129 	if (res == -1) {
130 		if (errno == EINVAL) {
131 			tst_brk(TCONF | TERRNO,
132 				"%s wasn't supported in quotactl()", desp);
133 		}
134 		tst_res(TFAIL | TERRNO,
135 			"quotactl() failed to get xfs disk quota limits");
136 		return;
137 	}
138 
139 	if (res_dquota.d_id != test_id) {
140 		tst_res(TFAIL, "quotactl() got unexpected user id %u,"
141 			" expected %u", res_dquota.d_id, test_id);
142 		return;
143 	}
144 
145 	if (res_dquota.d_rtb_hardlimit != set_dquota.d_rtb_hardlimit) {
146 		tst_res(TFAIL, "quotactl() got unexpected rtb soft limit %llu,"
147 			" expected %llu", res_dquota.d_rtb_hardlimit,
148 			set_dquota.d_rtb_hardlimit);
149 		return;
150 	}
151 
152 	tst_res(TPASS, "quoactl() succeeded to set and use %s to get xfs disk "
153 		"quota limits", desp);
154 }
155 
setup(void)156 static void setup(void)
157 {
158 	test_id = geteuid();
159 }
160 
verify_quota(unsigned int n)161 static void verify_quota(unsigned int n)
162 {
163 	struct t_case *tc = &tcases[n];
164 
165 	TEST(quotactl(tc->cmd, tst_device->dev, test_id, tc->addr));
166 	if (TST_RET == -1) {
167 		tst_res(TFAIL | TTERRNO, "quotactl() failed to %s", tc->des);
168 		return;
169 	}
170 
171 	tc->func_check(tc->check_subcmd, tc->des);
172 }
173 
174 static struct tst_test test = {
175 	.needs_tmpdir = 1,
176 	.needs_root = 1,
177 	.test = verify_quota,
178 	.tcnt = ARRAY_SIZE(tcases),
179 	.mount_device = 1,
180 	.dev_fs_type = "xfs",
181 	.mntpoint = mntpoint,
182 	.mnt_data = "usrquota",
183 	.setup = setup,
184 };
185 #else
186 	TST_TEST_TCONF("This system didn't support quota or xfs quota");
187 #endif
188