h_quota2_tests.c revision 1.6 1 1.6 rillig /* $NetBSD: h_quota2_tests.c,v 1.6 2024/09/08 09:36:52 rillig Exp $ */
2 1.2 bouyer
3 1.2 bouyer /*
4 1.2 bouyer * rump server for advanced quota tests
5 1.2 bouyer * this one includes functions to run against the filesystem before
6 1.2 bouyer * starting to handle rump requests from clients.
7 1.2 bouyer */
8 1.2 bouyer
9 1.2 bouyer #include "../common/h_fsmacros.h"
10 1.2 bouyer
11 1.2 bouyer #include <err.h>
12 1.2 bouyer #include <semaphore.h>
13 1.2 bouyer #include <sys/types.h>
14 1.2 bouyer #include <sys/mount.h>
15 1.2 bouyer
16 1.2 bouyer #include <stdlib.h>
17 1.2 bouyer #include <unistd.h>
18 1.2 bouyer
19 1.2 bouyer #include <ufs/ufs/ufsmount.h>
20 1.2 bouyer #include <dev/fssvar.h>
21 1.2 bouyer
22 1.2 bouyer #include <rump/rump.h>
23 1.2 bouyer #include <rump/rump_syscalls.h>
24 1.2 bouyer
25 1.5 christos #include "h_macros.h"
26 1.2 bouyer
27 1.2 bouyer int background = 0;
28 1.2 bouyer
29 1.2 bouyer #define TEST_NONROOT_ID 1
30 1.2 bouyer
31 1.2 bouyer static int
32 1.2 bouyer quota_test0(const char *testopts)
33 1.2 bouyer {
34 1.2 bouyer static char buf[512];
35 1.2 bouyer int fd;
36 1.2 bouyer int error;
37 1.4 bouyer unsigned int i;
38 1.4 bouyer int chowner = 1;
39 1.4 bouyer for (i =0; testopts && i < strlen(testopts); i++) {
40 1.4 bouyer switch(testopts[i]) {
41 1.4 bouyer case 'C':
42 1.4 bouyer chowner = 0;
43 1.4 bouyer break;
44 1.4 bouyer default:
45 1.4 bouyer errx(1, "test4: unknown option %c", testopts[i]);
46 1.4 bouyer }
47 1.4 bouyer }
48 1.4 bouyer if (chowner)
49 1.4 bouyer rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
50 1.2 bouyer rump_sys_chmod(".", 0777);
51 1.2 bouyer if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
52 1.2 bouyer error = errno;
53 1.3 christos warn("rump_sys_setegid");
54 1.2 bouyer return error;
55 1.2 bouyer }
56 1.2 bouyer if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
57 1.2 bouyer error = errno;
58 1.3 christos warn("rump_sys_seteuid");
59 1.2 bouyer return error;
60 1.2 bouyer }
61 1.2 bouyer fd = rump_sys_open("test_fillup", O_CREAT | O_RDWR, 0644);
62 1.2 bouyer if (fd < 0) {
63 1.2 bouyer error = errno;
64 1.3 christos warn("rump_sys_open");
65 1.2 bouyer } else {
66 1.2 bouyer while (rump_sys_write(fd, buf, sizeof(buf)) == sizeof(buf))
67 1.2 bouyer error = 0;
68 1.2 bouyer error = errno;
69 1.2 bouyer }
70 1.2 bouyer rump_sys_close(fd);
71 1.2 bouyer rump_sys_seteuid(0);
72 1.2 bouyer rump_sys_setegid(0);
73 1.2 bouyer return error;
74 1.2 bouyer }
75 1.2 bouyer
76 1.2 bouyer static int
77 1.2 bouyer quota_test1(const char *testopts)
78 1.2 bouyer {
79 1.2 bouyer static char buf[512];
80 1.2 bouyer int fd;
81 1.2 bouyer int error;
82 1.2 bouyer rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
83 1.2 bouyer rump_sys_chmod(".", 0777);
84 1.2 bouyer if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
85 1.2 bouyer error = errno;
86 1.3 christos warn("rump_sys_setegid");
87 1.2 bouyer return error;
88 1.2 bouyer }
89 1.2 bouyer if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
90 1.2 bouyer error = errno;
91 1.3 christos warn("rump_sys_seteuid");
92 1.2 bouyer return error;
93 1.2 bouyer }
94 1.2 bouyer fd = rump_sys_open("test_fillup", O_CREAT | O_RDWR, 0644);
95 1.2 bouyer if (fd < 0) {
96 1.2 bouyer error = errno;
97 1.3 christos warn("rump_sys_open");
98 1.2 bouyer } else {
99 1.2 bouyer /*
100 1.6 rillig * write up to the soft limit, wait a bit, and try to
101 1.2 bouyer * keep on writing
102 1.2 bouyer */
103 1.2 bouyer int i;
104 1.2 bouyer
105 1.2 bouyer /* write 2k: with the directory this makes 2.5K */
106 1.2 bouyer for (i = 0; i < 4; i++) {
107 1.2 bouyer error = rump_sys_write(fd, buf, sizeof(buf));
108 1.2 bouyer if (error != sizeof(buf))
109 1.2 bouyer err(1, "write failed early");
110 1.2 bouyer }
111 1.2 bouyer sleep(2);
112 1.2 bouyer /* now try to write an extra .5k */
113 1.2 bouyer if (rump_sys_write(fd, buf, sizeof(buf)) != sizeof(buf))
114 1.2 bouyer error = errno;
115 1.2 bouyer else
116 1.2 bouyer error = 0;
117 1.2 bouyer }
118 1.2 bouyer rump_sys_close(fd);
119 1.2 bouyer rump_sys_seteuid(0);
120 1.2 bouyer rump_sys_setegid(0);
121 1.2 bouyer return error;
122 1.2 bouyer }
123 1.2 bouyer
124 1.2 bouyer static int
125 1.2 bouyer quota_test2(const char *testopts)
126 1.2 bouyer {
127 1.2 bouyer static char buf[512];
128 1.2 bouyer int fd;
129 1.2 bouyer int error;
130 1.2 bouyer int i;
131 1.2 bouyer rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
132 1.2 bouyer rump_sys_chmod(".", 0777);
133 1.2 bouyer if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
134 1.2 bouyer error = errno;
135 1.3 christos warn("rump_sys_setegid");
136 1.2 bouyer return error;
137 1.2 bouyer }
138 1.2 bouyer if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
139 1.2 bouyer error = errno;
140 1.3 christos warn("rump_sys_seteuid");
141 1.2 bouyer return error;
142 1.2 bouyer }
143 1.2 bouyer
144 1.2 bouyer for (i = 0; ; i++) {
145 1.2 bouyer sprintf(buf, "file%d", i);
146 1.2 bouyer fd = rump_sys_open(buf, O_CREAT | O_RDWR, 0644);
147 1.2 bouyer if (fd < 0)
148 1.2 bouyer break;
149 1.2 bouyer sprintf(buf, "test file no %d", i);
150 1.2 bouyer rump_sys_write(fd, buf, strlen(buf));
151 1.2 bouyer rump_sys_close(fd);
152 1.2 bouyer }
153 1.2 bouyer error = errno;
154 1.2 bouyer
155 1.2 bouyer rump_sys_close(fd);
156 1.2 bouyer rump_sys_seteuid(0);
157 1.2 bouyer rump_sys_setegid(0);
158 1.2 bouyer return error;
159 1.2 bouyer }
160 1.2 bouyer
161 1.2 bouyer static int
162 1.2 bouyer quota_test3(const char *testopts)
163 1.2 bouyer {
164 1.2 bouyer static char buf[512];
165 1.2 bouyer int fd;
166 1.2 bouyer int error;
167 1.2 bouyer int i;
168 1.2 bouyer rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
169 1.2 bouyer rump_sys_chmod(".", 0777);
170 1.2 bouyer if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
171 1.2 bouyer error = errno;
172 1.3 christos warn("rump_sys_setegid");
173 1.2 bouyer return error;
174 1.2 bouyer }
175 1.2 bouyer if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
176 1.2 bouyer error = errno;
177 1.3 christos warn("rump_sys_seteuid");
178 1.2 bouyer return error;
179 1.2 bouyer }
180 1.2 bouyer
181 1.2 bouyer /*
182 1.2 bouyer * create files one past the soft limit: one less as we already own the
183 1.2 bouyer * root directory
184 1.2 bouyer */
185 1.2 bouyer for (i = 0; i < 4; i++) {
186 1.2 bouyer sprintf(buf, "file%d", i);
187 1.2 bouyer fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
188 1.2 bouyer if (fd < 0)
189 1.2 bouyer err(1, "file create failed early");
190 1.2 bouyer sprintf(buf, "test file no %d", i);
191 1.2 bouyer rump_sys_write(fd, buf, strlen(buf));
192 1.2 bouyer rump_sys_close(fd);
193 1.2 bouyer }
194 1.2 bouyer /* now create an extra file after grace time: this should fail */
195 1.2 bouyer sleep(2);
196 1.2 bouyer sprintf(buf, "file%d", i);
197 1.2 bouyer fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
198 1.2 bouyer if (fd < 0)
199 1.2 bouyer error = errno;
200 1.2 bouyer else
201 1.2 bouyer error = 0;
202 1.2 bouyer
203 1.2 bouyer rump_sys_close(fd);
204 1.2 bouyer rump_sys_seteuid(0);
205 1.2 bouyer rump_sys_setegid(0);
206 1.2 bouyer return error;
207 1.2 bouyer }
208 1.2 bouyer
209 1.2 bouyer static int
210 1.2 bouyer quota_test4(const char *testopts)
211 1.2 bouyer {
212 1.2 bouyer static char buf[512];
213 1.2 bouyer int fd, fssfd;
214 1.2 bouyer struct fss_set fss;
215 1.2 bouyer unsigned int i;
216 1.2 bouyer int unl=0;
217 1.2 bouyer int unconf=0;
218 1.2 bouyer
219 1.2 bouyer /*
220 1.2 bouyer * take an internal snapshot of the filesystem, and create a new
221 1.2 bouyer * file with some data
222 1.2 bouyer */
223 1.2 bouyer rump_sys_chown(".", 0, 0);
224 1.2 bouyer rump_sys_chmod(".", 0777);
225 1.2 bouyer
226 1.2 bouyer for (i =0; testopts && i < strlen(testopts); i++) {
227 1.2 bouyer switch(testopts[i]) {
228 1.2 bouyer case 'L':
229 1.2 bouyer unl++;
230 1.2 bouyer break;
231 1.2 bouyer case 'C':
232 1.2 bouyer unconf++;
233 1.2 bouyer break;
234 1.2 bouyer default:
235 1.2 bouyer errx(1, "test4: unknown option %c", testopts[i]);
236 1.2 bouyer }
237 1.2 bouyer }
238 1.2 bouyer
239 1.2 bouyer /* first create the snapshot */
240 1.2 bouyer
241 1.2 bouyer fd = rump_sys_open(FSTEST_MNTNAME "/le_snap", O_CREAT | O_RDWR, 0777);
242 1.2 bouyer if (fd == -1)
243 1.2 bouyer err(1, "create " FSTEST_MNTNAME "/le_snap");
244 1.2 bouyer rump_sys_close(fd);
245 1.2 bouyer fssfd = rump_sys_open("/dev/rfss0", O_RDWR);
246 1.2 bouyer if (fssfd == -1)
247 1.2 bouyer err(1, "cannot open fss");
248 1.2 bouyer memset(&fss, 0, sizeof(fss));
249 1.2 bouyer fss.fss_mount = __UNCONST("/mnt");
250 1.2 bouyer fss.fss_bstore = __UNCONST(FSTEST_MNTNAME "/le_snap");
251 1.2 bouyer fss.fss_csize = 0;
252 1.2 bouyer if (rump_sys_ioctl(fssfd, FSSIOCSET, &fss) == -1)
253 1.2 bouyer err(1, "create snapshot");
254 1.2 bouyer if (unl) {
255 1.2 bouyer if (rump_sys_unlink(FSTEST_MNTNAME "/le_snap") == -1)
256 1.2 bouyer err(1, "unlink snapshot");
257 1.2 bouyer }
258 1.2 bouyer
259 1.2 bouyer /* now create some extra files */
260 1.2 bouyer
261 1.2 bouyer for (i = 0; i < 4; i++) {
262 1.2 bouyer sprintf(buf, "file%d", i);
263 1.2 bouyer fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
264 1.2 bouyer if (fd < 0)
265 1.2 bouyer err(1, "create %s", buf);
266 1.2 bouyer sprintf(buf, "test file no %d", i);
267 1.2 bouyer rump_sys_write(fd, buf, strlen(buf));
268 1.2 bouyer rump_sys_close(fd);
269 1.2 bouyer }
270 1.2 bouyer if (unconf)
271 1.2 bouyer if (rump_sys_ioctl(fssfd, FSSIOCCLR, NULL) == -1)
272 1.2 bouyer err(1, "unconfigure snapshot");
273 1.2 bouyer return 0;
274 1.2 bouyer }
275 1.2 bouyer
276 1.2 bouyer static int
277 1.2 bouyer quota_test5(const char *testopts)
278 1.2 bouyer {
279 1.2 bouyer static char buf[512];
280 1.2 bouyer int fd;
281 1.2 bouyer int remount = 0;
282 1.2 bouyer int unlnk = 0;
283 1.2 bouyer int log = 0;
284 1.2 bouyer unsigned int i;
285 1.2 bouyer
286 1.2 bouyer for (i =0; testopts && i < strlen(testopts); i++) {
287 1.2 bouyer switch(testopts[i]) {
288 1.2 bouyer case 'L':
289 1.2 bouyer log++;
290 1.2 bouyer break;
291 1.2 bouyer case 'R':
292 1.2 bouyer remount++;
293 1.2 bouyer break;
294 1.2 bouyer case 'U':
295 1.2 bouyer unlnk++;
296 1.2 bouyer break;
297 1.2 bouyer default:
298 1.2 bouyer errx(1, "test4: unknown option %c", testopts[i]);
299 1.2 bouyer }
300 1.2 bouyer }
301 1.2 bouyer if (remount) {
302 1.2 bouyer struct ufs_args uargs;
303 1.2 bouyer uargs.fspec = __UNCONST("/diskdev");
304 1.2 bouyer /* remount the fs read/write */
305 1.2 bouyer if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME,
306 1.2 bouyer MNT_UPDATE | (log ? MNT_LOG : 0),
307 1.2 bouyer &uargs, sizeof(uargs)) == -1)
308 1.2 bouyer err(1, "mount ffs rw %s", FSTEST_MNTNAME);
309 1.2 bouyer }
310 1.2 bouyer
311 1.2 bouyer if (unlnk) {
312 1.2 bouyer /*
313 1.2 bouyer * open and unlink a file
314 1.2 bouyer */
315 1.2 bouyer
316 1.2 bouyer fd = rump_sys_open("unlinked_file",
317 1.2 bouyer O_EXCL| O_CREAT | O_RDWR, 0644);
318 1.2 bouyer if (fd < 0)
319 1.2 bouyer err(1, "create %s", "unlinked_file");
320 1.2 bouyer sprintf(buf, "test unlinked_file");
321 1.2 bouyer rump_sys_write(fd, buf, strlen(buf));
322 1.2 bouyer if (rump_sys_unlink("unlinked_file") == -1)
323 1.2 bouyer err(1, "unlink unlinked_file");
324 1.2 bouyer if (rump_sys_fsync(fd) == -1)
325 1.2 bouyer err(1, "fsync unlinked_file");
326 1.2 bouyer rump_sys_reboot(RUMP_RB_NOSYNC, NULL);
327 1.2 bouyer errx(1, "reboot failed");
328 1.2 bouyer return 1;
329 1.2 bouyer }
330 1.2 bouyer return 0;
331 1.2 bouyer }
332 1.2 bouyer
333 1.2 bouyer struct quota_test {
334 1.2 bouyer int (*func)(const char *);
335 1.2 bouyer const char *desc;
336 1.2 bouyer };
337 1.2 bouyer
338 1.2 bouyer struct quota_test quota_tests[] = {
339 1.2 bouyer { quota_test0, "write up to hard limit"},
340 1.2 bouyer { quota_test1, "write beyond the soft limit after grace time"},
341 1.2 bouyer { quota_test2, "create file up to hard limit"},
342 1.2 bouyer { quota_test3, "create file beyond the soft limit after grace time"},
343 1.2 bouyer { quota_test4, "take a snapshot and add some data"},
344 1.2 bouyer { quota_test5, "open and unlink a file"},
345 1.2 bouyer };
346 1.2 bouyer
347 1.2 bouyer static void
348 1.2 bouyer usage(void)
349 1.2 bouyer {
350 1.2 bouyer unsigned int test;
351 1.2 bouyer fprintf(stderr, "usage: %s [-b] [-l] test# diskimage bindurl\n",
352 1.2 bouyer getprogname());
353 1.2 bouyer fprintf(stderr, "available tests:\n");
354 1.2 bouyer for (test = 0; test < sizeof(quota_tests) / sizeof(quota_tests[0]);
355 1.2 bouyer test++)
356 1.2 bouyer fprintf(stderr, "\t%d: %s\n", test, quota_tests[test].desc);
357 1.2 bouyer exit(1);
358 1.2 bouyer }
359 1.2 bouyer
360 1.2 bouyer static void
361 1.2 bouyer die(const char *reason, int error)
362 1.2 bouyer {
363 1.2 bouyer
364 1.2 bouyer warnx("%s: %s", reason, strerror(error));
365 1.2 bouyer if (background)
366 1.2 bouyer rump_daemonize_done(error);
367 1.2 bouyer exit(1);
368 1.2 bouyer }
369 1.2 bouyer
370 1.2 bouyer static sem_t sigsem;
371 1.2 bouyer static void
372 1.2 bouyer sigreboot(int sig)
373 1.2 bouyer {
374 1.2 bouyer
375 1.2 bouyer sem_post(&sigsem);
376 1.2 bouyer }
377 1.2 bouyer
378 1.2 bouyer int
379 1.2 bouyer main(int argc, char **argv)
380 1.2 bouyer {
381 1.2 bouyer int error;
382 1.2 bouyer u_long test;
383 1.2 bouyer char *end;
384 1.2 bouyer struct ufs_args uargs;
385 1.2 bouyer const char *filename;
386 1.2 bouyer const char *serverurl;
387 1.2 bouyer const char *topts = NULL;
388 1.2 bouyer int mntopts = 0;
389 1.2 bouyer int ch;
390 1.2 bouyer
391 1.2 bouyer while ((ch = getopt(argc, argv, "blo:r")) != -1) {
392 1.2 bouyer switch(ch) {
393 1.2 bouyer case 'b':
394 1.2 bouyer background = 1;
395 1.2 bouyer break;
396 1.2 bouyer case 'l':
397 1.2 bouyer mntopts |= MNT_LOG;
398 1.2 bouyer break;
399 1.2 bouyer case 'r':
400 1.2 bouyer mntopts |= MNT_RDONLY;
401 1.2 bouyer break;
402 1.2 bouyer case 'o':
403 1.2 bouyer topts = optarg;
404 1.2 bouyer break;
405 1.2 bouyer default:
406 1.2 bouyer usage();
407 1.2 bouyer }
408 1.2 bouyer }
409 1.2 bouyer argc -= optind;
410 1.2 bouyer argv += optind;
411 1.2 bouyer
412 1.2 bouyer if (argc != 3)
413 1.2 bouyer usage();
414 1.2 bouyer
415 1.2 bouyer filename = argv[1];
416 1.2 bouyer serverurl = argv[2];
417 1.2 bouyer
418 1.2 bouyer test = strtoul(argv[0], &end, 10);
419 1.2 bouyer if (*end != '\0') {
420 1.2 bouyer usage();
421 1.2 bouyer }
422 1.2 bouyer if (test > sizeof(quota_tests) / sizeof(quota_tests[0])) {
423 1.2 bouyer usage();
424 1.2 bouyer }
425 1.2 bouyer
426 1.2 bouyer if (background) {
427 1.2 bouyer error = rump_daemonize_begin();
428 1.2 bouyer if (error)
429 1.2 bouyer errx(1, "rump daemonize: %s", strerror(error));
430 1.2 bouyer }
431 1.2 bouyer
432 1.2 bouyer error = rump_init();
433 1.2 bouyer if (error)
434 1.2 bouyer die("rump init failed", error);
435 1.2 bouyer
436 1.2 bouyer if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1)
437 1.2 bouyer err(1, "mount point create");
438 1.2 bouyer rump_pub_etfs_register("/diskdev", filename, RUMP_ETFS_BLK);
439 1.2 bouyer uargs.fspec = __UNCONST("/diskdev");
440 1.2 bouyer if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, mntopts,
441 1.2 bouyer &uargs, sizeof(uargs)) == -1)
442 1.2 bouyer die("mount ffs", errno);
443 1.2 bouyer
444 1.2 bouyer if (rump_sys_chdir(FSTEST_MNTNAME) == -1)
445 1.2 bouyer err(1, "cd %s", FSTEST_MNTNAME);
446 1.2 bouyer error = quota_tests[test].func(topts);
447 1.2 bouyer if (error) {
448 1.2 bouyer fprintf(stderr, " test %lu: %s returned %d: %s\n",
449 1.2 bouyer test, quota_tests[test].desc, error, strerror(error));
450 1.2 bouyer }
451 1.2 bouyer if (rump_sys_chdir("/") == -1)
452 1.2 bouyer err(1, "cd /");
453 1.2 bouyer
454 1.2 bouyer error = rump_init_server(serverurl);
455 1.2 bouyer if (error)
456 1.2 bouyer die("rump server init failed", error);
457 1.2 bouyer if (background)
458 1.2 bouyer rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS);
459 1.2 bouyer
460 1.2 bouyer sem_init(&sigsem, 0, 0);
461 1.2 bouyer signal(SIGTERM, sigreboot);
462 1.2 bouyer signal(SIGINT, sigreboot);
463 1.2 bouyer sem_wait(&sigsem);
464 1.2 bouyer
465 1.2 bouyer rump_sys_reboot(0, NULL);
466 1.2 bouyer /*NOTREACHED*/
467 1.2 bouyer return 0;
468 1.2 bouyer }
469