f800b89bbc
when a on-disk block/inode allocation triggers allocating a new quota entry, the new quota entry is not in the quota2 header block, and the allocation will later be denied, the changes to the quota block would not be flushed to disk, leading to list corruption (detected by fsck).
469 lines
9.7 KiB
C
469 lines
9.7 KiB
C
/* $NetBSD: h_quota2_tests.c,v 1.4 2012/09/30 21:26:57 bouyer Exp $ */
|
|
|
|
/*
|
|
* rump server for advanced quota tests
|
|
* this one includes functions to run against the filesystem before
|
|
* starting to handle rump requests from clients.
|
|
*/
|
|
|
|
#include "../common/h_fsmacros.h"
|
|
|
|
#include <err.h>
|
|
#include <semaphore.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mount.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <ufs/ufs/ufsmount.h>
|
|
#include <dev/fssvar.h>
|
|
|
|
#include <rump/rump.h>
|
|
#include <rump/rump_syscalls.h>
|
|
|
|
#include "../../h_macros.h"
|
|
|
|
int background = 0;
|
|
|
|
#define TEST_NONROOT_ID 1
|
|
|
|
static int
|
|
quota_test0(const char *testopts)
|
|
{
|
|
static char buf[512];
|
|
int fd;
|
|
int error;
|
|
unsigned int i;
|
|
int chowner = 1;
|
|
for (i =0; testopts && i < strlen(testopts); i++) {
|
|
switch(testopts[i]) {
|
|
case 'C':
|
|
chowner = 0;
|
|
break;
|
|
default:
|
|
errx(1, "test4: unknown option %c", testopts[i]);
|
|
}
|
|
}
|
|
if (chowner)
|
|
rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
|
|
rump_sys_chmod(".", 0777);
|
|
if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_setegid");
|
|
return error;
|
|
}
|
|
if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_seteuid");
|
|
return error;
|
|
}
|
|
fd = rump_sys_open("test_fillup", O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0) {
|
|
error = errno;
|
|
warn("rump_sys_open");
|
|
} else {
|
|
while (rump_sys_write(fd, buf, sizeof(buf)) == sizeof(buf))
|
|
error = 0;
|
|
error = errno;
|
|
}
|
|
rump_sys_close(fd);
|
|
rump_sys_seteuid(0);
|
|
rump_sys_setegid(0);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
quota_test1(const char *testopts)
|
|
{
|
|
static char buf[512];
|
|
int fd;
|
|
int error;
|
|
rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
|
|
rump_sys_chmod(".", 0777);
|
|
if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_setegid");
|
|
return error;
|
|
}
|
|
if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_seteuid");
|
|
return error;
|
|
}
|
|
fd = rump_sys_open("test_fillup", O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0) {
|
|
error = errno;
|
|
warn("rump_sys_open");
|
|
} else {
|
|
/*
|
|
* write up to the soft limit, wait a bit, an try to
|
|
* keep on writing
|
|
*/
|
|
int i;
|
|
|
|
/* write 2k: with the directory this makes 2.5K */
|
|
for (i = 0; i < 4; i++) {
|
|
error = rump_sys_write(fd, buf, sizeof(buf));
|
|
if (error != sizeof(buf))
|
|
err(1, "write failed early");
|
|
}
|
|
sleep(2);
|
|
/* now try to write an extra .5k */
|
|
if (rump_sys_write(fd, buf, sizeof(buf)) != sizeof(buf))
|
|
error = errno;
|
|
else
|
|
error = 0;
|
|
}
|
|
rump_sys_close(fd);
|
|
rump_sys_seteuid(0);
|
|
rump_sys_setegid(0);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
quota_test2(const char *testopts)
|
|
{
|
|
static char buf[512];
|
|
int fd;
|
|
int error;
|
|
int i;
|
|
rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
|
|
rump_sys_chmod(".", 0777);
|
|
if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_setegid");
|
|
return error;
|
|
}
|
|
if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_seteuid");
|
|
return error;
|
|
}
|
|
|
|
for (i = 0; ; i++) {
|
|
sprintf(buf, "file%d", i);
|
|
fd = rump_sys_open(buf, O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
break;
|
|
sprintf(buf, "test file no %d", i);
|
|
rump_sys_write(fd, buf, strlen(buf));
|
|
rump_sys_close(fd);
|
|
}
|
|
error = errno;
|
|
|
|
rump_sys_close(fd);
|
|
rump_sys_seteuid(0);
|
|
rump_sys_setegid(0);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
quota_test3(const char *testopts)
|
|
{
|
|
static char buf[512];
|
|
int fd;
|
|
int error;
|
|
int i;
|
|
rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
|
|
rump_sys_chmod(".", 0777);
|
|
if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_setegid");
|
|
return error;
|
|
}
|
|
if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
|
|
error = errno;
|
|
warn("rump_sys_seteuid");
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* create files one past the soft limit: one less as we already own the
|
|
* root directory
|
|
*/
|
|
for (i = 0; i < 4; i++) {
|
|
sprintf(buf, "file%d", i);
|
|
fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
err(1, "file create failed early");
|
|
sprintf(buf, "test file no %d", i);
|
|
rump_sys_write(fd, buf, strlen(buf));
|
|
rump_sys_close(fd);
|
|
}
|
|
/* now create an extra file after grace time: this should fail */
|
|
sleep(2);
|
|
sprintf(buf, "file%d", i);
|
|
fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
error = errno;
|
|
else
|
|
error = 0;
|
|
|
|
rump_sys_close(fd);
|
|
rump_sys_seteuid(0);
|
|
rump_sys_setegid(0);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
quota_test4(const char *testopts)
|
|
{
|
|
static char buf[512];
|
|
int fd, fssfd;
|
|
struct fss_set fss;
|
|
unsigned int i;
|
|
int unl=0;
|
|
int unconf=0;
|
|
|
|
/*
|
|
* take an internal snapshot of the filesystem, and create a new
|
|
* file with some data
|
|
*/
|
|
rump_sys_chown(".", 0, 0);
|
|
rump_sys_chmod(".", 0777);
|
|
|
|
for (i =0; testopts && i < strlen(testopts); i++) {
|
|
switch(testopts[i]) {
|
|
case 'L':
|
|
unl++;
|
|
break;
|
|
case 'C':
|
|
unconf++;
|
|
break;
|
|
default:
|
|
errx(1, "test4: unknown option %c", testopts[i]);
|
|
}
|
|
}
|
|
|
|
/* first create the snapshot */
|
|
|
|
fd = rump_sys_open(FSTEST_MNTNAME "/le_snap", O_CREAT | O_RDWR, 0777);
|
|
if (fd == -1)
|
|
err(1, "create " FSTEST_MNTNAME "/le_snap");
|
|
rump_sys_close(fd);
|
|
fssfd = rump_sys_open("/dev/rfss0", O_RDWR);
|
|
if (fssfd == -1)
|
|
err(1, "cannot open fss");
|
|
memset(&fss, 0, sizeof(fss));
|
|
fss.fss_mount = __UNCONST("/mnt");
|
|
fss.fss_bstore = __UNCONST(FSTEST_MNTNAME "/le_snap");
|
|
fss.fss_csize = 0;
|
|
if (rump_sys_ioctl(fssfd, FSSIOCSET, &fss) == -1)
|
|
err(1, "create snapshot");
|
|
if (unl) {
|
|
if (rump_sys_unlink(FSTEST_MNTNAME "/le_snap") == -1)
|
|
err(1, "unlink snapshot");
|
|
}
|
|
|
|
/* now create some extra files */
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
sprintf(buf, "file%d", i);
|
|
fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
err(1, "create %s", buf);
|
|
sprintf(buf, "test file no %d", i);
|
|
rump_sys_write(fd, buf, strlen(buf));
|
|
rump_sys_close(fd);
|
|
}
|
|
if (unconf)
|
|
if (rump_sys_ioctl(fssfd, FSSIOCCLR, NULL) == -1)
|
|
err(1, "unconfigure snapshot");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
quota_test5(const char *testopts)
|
|
{
|
|
static char buf[512];
|
|
int fd;
|
|
int remount = 0;
|
|
int unlnk = 0;
|
|
int log = 0;
|
|
unsigned int i;
|
|
|
|
for (i =0; testopts && i < strlen(testopts); i++) {
|
|
switch(testopts[i]) {
|
|
case 'L':
|
|
log++;
|
|
break;
|
|
case 'R':
|
|
remount++;
|
|
break;
|
|
case 'U':
|
|
unlnk++;
|
|
break;
|
|
default:
|
|
errx(1, "test4: unknown option %c", testopts[i]);
|
|
}
|
|
}
|
|
if (remount) {
|
|
struct ufs_args uargs;
|
|
uargs.fspec = __UNCONST("/diskdev");
|
|
/* remount the fs read/write */
|
|
if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME,
|
|
MNT_UPDATE | (log ? MNT_LOG : 0),
|
|
&uargs, sizeof(uargs)) == -1)
|
|
err(1, "mount ffs rw %s", FSTEST_MNTNAME);
|
|
}
|
|
|
|
if (unlnk) {
|
|
/*
|
|
* open and unlink a file
|
|
*/
|
|
|
|
fd = rump_sys_open("unlinked_file",
|
|
O_EXCL| O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
err(1, "create %s", "unlinked_file");
|
|
sprintf(buf, "test unlinked_file");
|
|
rump_sys_write(fd, buf, strlen(buf));
|
|
if (rump_sys_unlink("unlinked_file") == -1)
|
|
err(1, "unlink unlinked_file");
|
|
if (rump_sys_fsync(fd) == -1)
|
|
err(1, "fsync unlinked_file");
|
|
rump_sys_reboot(RUMP_RB_NOSYNC, NULL);
|
|
errx(1, "reboot failed");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct quota_test {
|
|
int (*func)(const char *);
|
|
const char *desc;
|
|
};
|
|
|
|
struct quota_test quota_tests[] = {
|
|
{ quota_test0, "write up to hard limit"},
|
|
{ quota_test1, "write beyond the soft limit after grace time"},
|
|
{ quota_test2, "create file up to hard limit"},
|
|
{ quota_test3, "create file beyond the soft limit after grace time"},
|
|
{ quota_test4, "take a snapshot and add some data"},
|
|
{ quota_test5, "open and unlink a file"},
|
|
};
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
unsigned int test;
|
|
fprintf(stderr, "usage: %s [-b] [-l] test# diskimage bindurl\n",
|
|
getprogname());
|
|
fprintf(stderr, "available tests:\n");
|
|
for (test = 0; test < sizeof(quota_tests) / sizeof(quota_tests[0]);
|
|
test++)
|
|
fprintf(stderr, "\t%d: %s\n", test, quota_tests[test].desc);
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
die(const char *reason, int error)
|
|
{
|
|
|
|
warnx("%s: %s", reason, strerror(error));
|
|
if (background)
|
|
rump_daemonize_done(error);
|
|
exit(1);
|
|
}
|
|
|
|
static sem_t sigsem;
|
|
static void
|
|
sigreboot(int sig)
|
|
{
|
|
|
|
sem_post(&sigsem);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int error;
|
|
u_long test;
|
|
char *end;
|
|
struct ufs_args uargs;
|
|
const char *filename;
|
|
const char *serverurl;
|
|
const char *topts = NULL;
|
|
int mntopts = 0;
|
|
int ch;
|
|
|
|
while ((ch = getopt(argc, argv, "blo:r")) != -1) {
|
|
switch(ch) {
|
|
case 'b':
|
|
background = 1;
|
|
break;
|
|
case 'l':
|
|
mntopts |= MNT_LOG;
|
|
break;
|
|
case 'r':
|
|
mntopts |= MNT_RDONLY;
|
|
break;
|
|
case 'o':
|
|
topts = optarg;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 3)
|
|
usage();
|
|
|
|
filename = argv[1];
|
|
serverurl = argv[2];
|
|
|
|
test = strtoul(argv[0], &end, 10);
|
|
if (*end != '\0') {
|
|
usage();
|
|
}
|
|
if (test > sizeof(quota_tests) / sizeof(quota_tests[0])) {
|
|
usage();
|
|
}
|
|
|
|
if (background) {
|
|
error = rump_daemonize_begin();
|
|
if (error)
|
|
errx(1, "rump daemonize: %s", strerror(error));
|
|
}
|
|
|
|
error = rump_init();
|
|
if (error)
|
|
die("rump init failed", error);
|
|
|
|
if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1)
|
|
err(1, "mount point create");
|
|
rump_pub_etfs_register("/diskdev", filename, RUMP_ETFS_BLK);
|
|
uargs.fspec = __UNCONST("/diskdev");
|
|
if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, mntopts,
|
|
&uargs, sizeof(uargs)) == -1)
|
|
die("mount ffs", errno);
|
|
|
|
if (rump_sys_chdir(FSTEST_MNTNAME) == -1)
|
|
err(1, "cd %s", FSTEST_MNTNAME);
|
|
error = quota_tests[test].func(topts);
|
|
if (error) {
|
|
fprintf(stderr, " test %lu: %s returned %d: %s\n",
|
|
test, quota_tests[test].desc, error, strerror(error));
|
|
}
|
|
if (rump_sys_chdir("/") == -1)
|
|
err(1, "cd /");
|
|
|
|
error = rump_init_server(serverurl);
|
|
if (error)
|
|
die("rump server init failed", error);
|
|
if (background)
|
|
rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS);
|
|
|
|
sem_init(&sigsem, 0, 0);
|
|
signal(SIGTERM, sigreboot);
|
|
signal(SIGINT, sigreboot);
|
|
sem_wait(&sigsem);
|
|
|
|
rump_sys_reboot(0, NULL);
|
|
/*NOTREACHED*/
|
|
return 0;
|
|
}
|