1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
4f08c3bdfSopenharmony_ci * Author: Yang Xu <xuyang2018.jy@fujitsu.com>
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Tests basic error handling of the quotactl syscall without visible quota files
11f08c3bdfSopenharmony_ci * (use quotactl and quotactl_fd syscall):
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * - EFAULT when addr or special is invalid
14f08c3bdfSopenharmony_ci * - EINVAL when cmd or type is invalid
15f08c3bdfSopenharmony_ci * - ENOTBLK when special is not a block device
16f08c3bdfSopenharmony_ci * - ERANGE when cmd is Q_SETQUOTA, but the specified limits are out of the range
17f08c3bdfSopenharmony_ci *   allowed by the quota format
18f08c3bdfSopenharmony_ci * - EPERM when the caller lacked the required privilege (CAP_SYS_ADMIN) for the
19f08c3bdfSopenharmony_ci *   specified operation
20f08c3bdfSopenharmony_ci * - ENOSYS when cmd is Q_QUOTAON, but the fd refers to a socket
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * Minimum e2fsprogs version required is 1.43.
23f08c3bdfSopenharmony_ci */
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_ci#include <errno.h>
26f08c3bdfSopenharmony_ci#include <sys/quota.h>
27f08c3bdfSopenharmony_ci#include <sys/socket.h>
28f08c3bdfSopenharmony_ci#include "tst_test.h"
29f08c3bdfSopenharmony_ci#include "tst_capability.h"
30f08c3bdfSopenharmony_ci#include "quotactl_syscall_var.h"
31f08c3bdfSopenharmony_ci
32f08c3bdfSopenharmony_ci#define OPTION_INVALID 999
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_cistatic int32_t fmt_id = QFMT_VFS_V1;
35f08c3bdfSopenharmony_cistatic int test_id;
36f08c3bdfSopenharmony_cistatic int getnextquota_nsup, socket_fd = -1;
37f08c3bdfSopenharmony_ci
38f08c3bdfSopenharmony_cistatic struct if_nextdqblk res_ndq;
39f08c3bdfSopenharmony_ci
40f08c3bdfSopenharmony_cistatic struct dqblk set_dqmax = {
41f08c3bdfSopenharmony_ci	.dqb_bsoftlimit = 0x7fffffffffffffffLL,  /* 2^63-1 */
42f08c3bdfSopenharmony_ci	.dqb_valid = QIF_BLIMITS
43f08c3bdfSopenharmony_ci};
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_cistatic struct tst_cap dropadmin = {
46f08c3bdfSopenharmony_ci	.action = TST_CAP_DROP,
47f08c3bdfSopenharmony_ci	.id = CAP_SYS_ADMIN,
48f08c3bdfSopenharmony_ci	.name = "CAP_SYS_ADMIN",
49f08c3bdfSopenharmony_ci};
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_cistatic struct tst_cap needadmin = {
52f08c3bdfSopenharmony_ci	.action = TST_CAP_REQ,
53f08c3bdfSopenharmony_ci	.id = CAP_SYS_ADMIN,
54f08c3bdfSopenharmony_ci	.name = "CAP_SYS_ADMIN",
55f08c3bdfSopenharmony_ci};
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_cistatic struct tcase {
58f08c3bdfSopenharmony_ci	int cmd;
59f08c3bdfSopenharmony_ci	int *id;
60f08c3bdfSopenharmony_ci	void *addr;
61f08c3bdfSopenharmony_ci	int exp_err;
62f08c3bdfSopenharmony_ci	int on_flag;
63f08c3bdfSopenharmony_ci	char *des;
64f08c3bdfSopenharmony_ci} tcases[] = {
65f08c3bdfSopenharmony_ci	{QCMD(Q_SETQUOTA, USRQUOTA), &fmt_id, NULL, EFAULT, 1,
66f08c3bdfSopenharmony_ci	"EFAULT when addr or special is invalid"},
67f08c3bdfSopenharmony_ci
68f08c3bdfSopenharmony_ci	{QCMD(OPTION_INVALID, USRQUOTA), &fmt_id, NULL, EINVAL, 0,
69f08c3bdfSopenharmony_ci	"EINVAL when cmd or type is invalid"},
70f08c3bdfSopenharmony_ci
71f08c3bdfSopenharmony_ci	{QCMD(Q_QUOTAON, USRQUOTA), &fmt_id, NULL, ENOTBLK, 0,
72f08c3bdfSopenharmony_ci	"ENOTBLK when special is not a block device"},
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	{QCMD(Q_SETQUOTA, USRQUOTA), &test_id, &set_dqmax, ERANGE, 1,
75f08c3bdfSopenharmony_ci	"ERANGE when cmd is Q_SETQUOTA, but the specified limits are out of the range"},
76f08c3bdfSopenharmony_ci
77f08c3bdfSopenharmony_ci	{QCMD(Q_QUOTAON, USRQUOTA), &fmt_id, NULL, EPERM, 0,
78f08c3bdfSopenharmony_ci	"EPERM when the caller lacks required privilege(CAP_SYS_ADMIN)"},
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_ci	{QCMD(Q_QUOTAON, USRQUOTA), &fmt_id, NULL, ENOSYS, 0,
81f08c3bdfSopenharmony_ci	"EINVAL when cmd is Q_QUOTAON, but the fd refers to a socket"}
82f08c3bdfSopenharmony_ci};
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_cistatic void verify_quotactl(unsigned int n)
85f08c3bdfSopenharmony_ci{
86f08c3bdfSopenharmony_ci	struct tcase *tc = &tcases[n];
87f08c3bdfSopenharmony_ci	int quota_on = 0;
88f08c3bdfSopenharmony_ci	int drop_flag = 0;
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ci	tst_res(TINFO, "Testing %s", tc->des);
91f08c3bdfSopenharmony_ci	if (tc->cmd == QCMD(Q_GETNEXTQUOTA, USRQUOTA) && getnextquota_nsup) {
92f08c3bdfSopenharmony_ci		tst_res(TCONF, "current system doesn't support Q_GETNEXTQUOTA");
93f08c3bdfSopenharmony_ci		return;
94f08c3bdfSopenharmony_ci	}
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci	if (tc->on_flag) {
97f08c3bdfSopenharmony_ci		TST_EXP_PASS_SILENT(do_quotactl(fd, QCMD(Q_QUOTAON, USRQUOTA), tst_device->dev,
98f08c3bdfSopenharmony_ci			fmt_id, NULL), "do_quotactl(QCMD(Q_QUOTAON, USRQUOTA))");
99f08c3bdfSopenharmony_ci		if (!TST_PASS)
100f08c3bdfSopenharmony_ci			return;
101f08c3bdfSopenharmony_ci		quota_on = 1;
102f08c3bdfSopenharmony_ci	}
103f08c3bdfSopenharmony_ci
104f08c3bdfSopenharmony_ci	if (tc->exp_err == EPERM) {
105f08c3bdfSopenharmony_ci		tst_cap_action(&dropadmin);
106f08c3bdfSopenharmony_ci		drop_flag = 1;
107f08c3bdfSopenharmony_ci	}
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_ci	if (tst_variant) {
110f08c3bdfSopenharmony_ci		if (tc->exp_err == ENOTBLK) {
111f08c3bdfSopenharmony_ci			tst_res(TCONF, "quotactl_fd() doesn't have this error, skip");
112f08c3bdfSopenharmony_ci			return;
113f08c3bdfSopenharmony_ci		}
114f08c3bdfSopenharmony_ci		if (tc->exp_err == ENOSYS) {
115f08c3bdfSopenharmony_ci			TST_EXP_FAIL(syscall(__NR_quotactl_fd, socket_fd, tc->cmd, *tc->id,
116f08c3bdfSopenharmony_ci				tc->addr), tc->exp_err, "syscall(quotactl_fd)");
117f08c3bdfSopenharmony_ci			return;
118f08c3bdfSopenharmony_ci		}
119f08c3bdfSopenharmony_ci	} else {
120f08c3bdfSopenharmony_ci		if (tc->exp_err == ENOSYS) {
121f08c3bdfSopenharmony_ci			tst_res(TCONF, "quotactl() doesn't use fd, skip");
122f08c3bdfSopenharmony_ci			return;
123f08c3bdfSopenharmony_ci		}
124f08c3bdfSopenharmony_ci	}
125f08c3bdfSopenharmony_ci	if (tc->exp_err == ENOTBLK)
126f08c3bdfSopenharmony_ci		TST_EXP_FAIL(do_quotactl(fd, tc->cmd, "/dev/null", *tc->id, tc->addr),
127f08c3bdfSopenharmony_ci			ENOTBLK, "do_quotactl()");
128f08c3bdfSopenharmony_ci	else
129f08c3bdfSopenharmony_ci		TST_EXP_FAIL(do_quotactl(fd, tc->cmd, tst_device->dev, *tc->id, tc->addr),
130f08c3bdfSopenharmony_ci			tc->exp_err, "do_quotactl()");
131f08c3bdfSopenharmony_ci
132f08c3bdfSopenharmony_ci	if (quota_on) {
133f08c3bdfSopenharmony_ci		TST_EXP_PASS_SILENT(do_quotactl(fd, QCMD(Q_QUOTAOFF, USRQUOTA), tst_device->dev,
134f08c3bdfSopenharmony_ci			fmt_id, NULL), "do_quotactl(QCMD(Q_QUOTAOFF, USRQUOTA)");
135f08c3bdfSopenharmony_ci		if (!TST_PASS)
136f08c3bdfSopenharmony_ci			return;
137f08c3bdfSopenharmony_ci	}
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci	if (drop_flag)
140f08c3bdfSopenharmony_ci		tst_cap_action(&needadmin);
141f08c3bdfSopenharmony_ci}
142f08c3bdfSopenharmony_ci
143f08c3bdfSopenharmony_cistatic void setup(void)
144f08c3bdfSopenharmony_ci{
145f08c3bdfSopenharmony_ci	unsigned int i;
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_ci	quotactl_info();
148f08c3bdfSopenharmony_ci
149f08c3bdfSopenharmony_ci	socket_fd = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0);
150f08c3bdfSopenharmony_ci	fd = SAFE_OPEN(MNTPOINT, O_RDONLY);
151f08c3bdfSopenharmony_ci	TEST(do_quotactl(fd, QCMD(Q_GETNEXTQUOTA, USRQUOTA), tst_device->dev,
152f08c3bdfSopenharmony_ci		test_id, (void *) &res_ndq));
153f08c3bdfSopenharmony_ci	if (TST_ERR == EINVAL || TST_ERR == ENOSYS)
154f08c3bdfSopenharmony_ci		getnextquota_nsup = 1;
155f08c3bdfSopenharmony_ci
156f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tcases); i++) {
157f08c3bdfSopenharmony_ci		if (!tcases[i].addr)
158f08c3bdfSopenharmony_ci			tcases[i].addr = tst_get_bad_addr(NULL);
159f08c3bdfSopenharmony_ci	}
160f08c3bdfSopenharmony_ci}
161f08c3bdfSopenharmony_ci
162f08c3bdfSopenharmony_cistatic void cleanup(void)
163f08c3bdfSopenharmony_ci{
164f08c3bdfSopenharmony_ci	if (fd > -1)
165f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
166f08c3bdfSopenharmony_ci	if (socket_fd > -1)
167f08c3bdfSopenharmony_ci		SAFE_CLOSE(socket_fd);
168f08c3bdfSopenharmony_ci}
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_cistatic struct tst_test test = {
171f08c3bdfSopenharmony_ci	.setup = setup,
172f08c3bdfSopenharmony_ci	.cleanup = cleanup,
173f08c3bdfSopenharmony_ci	.needs_drivers = (const char *const []) {
174f08c3bdfSopenharmony_ci		"quota_v2",
175f08c3bdfSopenharmony_ci		NULL
176f08c3bdfSopenharmony_ci	},
177f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tcases),
178f08c3bdfSopenharmony_ci	.test = verify_quotactl,
179f08c3bdfSopenharmony_ci	.dev_fs_opts = (const char *const[]){"-O quota", NULL},
180f08c3bdfSopenharmony_ci	.dev_fs_type = "ext4",
181f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
182f08c3bdfSopenharmony_ci	.mount_device = 1,
183f08c3bdfSopenharmony_ci	.needs_root = 1,
184f08c3bdfSopenharmony_ci	.test_variants = QUOTACTL_SYSCALL_VARIANTS,
185f08c3bdfSopenharmony_ci	.needs_cmds = (const char *[]) {
186f08c3bdfSopenharmony_ci		"mkfs.ext4 >= 1.43.0",
187f08c3bdfSopenharmony_ci		NULL
188f08c3bdfSopenharmony_ci	}
189f08c3bdfSopenharmony_ci};
190