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