Viewing: group_lock_test.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, 2015 Cray Inc, all rights reserved.
* Author: Frank Zago.
*
* A few portions are extracted from llapi_layout_test.c
*
* The purpose of this test is to exert the group lock ioctls.
*
* The program will exit as soon as a non zero error code is returned.
*/
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <poll.h>
#include <lustre/lustreapi.h>
#define ERROR(fmt, ...) \
fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
program_invocation_short_name, __FILE__, __LINE__, \
__func__, ## __VA_ARGS__);
#define DIE(fmt, ...) \
do { \
ERROR(fmt, ## __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
#define ASSERTF(cond, fmt, ...) \
do { \
if (!(cond)) \
DIE("assertion '%s' failed: "fmt, \
#cond, ## __VA_ARGS__); \
} while (0)
#define PERFORM(testfn) \
do { \
cleanup(); \
fprintf(stderr, "Starting test " #testfn " at %lld\n", \
(unsigned long long)time(NULL)); \
testfn(); \
fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
(unsigned long long)time(NULL)); \
cleanup(); \
} while (0)
/* Name of file/directory. Will be set once and will not change. */
static char mainpath[PATH_MAX];
static const char *maindir = "group_lock_test_name_9585766";
static char fsmountdir[PATH_MAX]; /* Lustre mountpoint */
static char *lustre_dir; /* Test directory inside Lustre */
/* Cleanup our test file. */
static void cleanup(void)
{
unlink(mainpath);
rmdir(mainpath);
}
/* Test lock / unlock */
static void test10(void)
{
int rc;
int fd;
int gid;
int i;
/* Create the test file, and open it. */
fd = creat(mainpath, 0);
ASSERTF(fd >= 0, "creat failed for '%s': %s",
mainpath, strerror(errno));
/* Valid command first. */
gid = 1234;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s",
mainpath, strerror(errno));
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s",
mainpath, strerror(errno));
/* Again */
gid = 768;
for (i = 0; i < 1000; i++) {
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0,
"cannot lock '%s': %s (loop %d)",
mainpath, strerror(errno), i);
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0,
"cannot unlock '%s': %s (loop %d)",
mainpath, strerror(errno), i);
}
/* Lock twice. */
gid = 97486;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == -1 && errno == EINVAL,
"lock unexpectedly granted for '%s': %s",
mainpath, strerror(errno));
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == -1 && errno == EINVAL, "unexpected unlock retval: %d %s",
rc, strerror(errno));
/* 0 is an invalid gid */
gid = 0;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == -1 && errno == EINVAL, "unexpected lock retval: %s",
strerror(errno));
/* Lock/unlock with a different gid */
gid = 3543;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
for (gid = -10; gid < 10; gid++) {
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected unlock retval: %d %s (gid %d)",
rc, strerror(errno), gid);
}
gid = 3543;
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
close(fd);
}
/* Test open/lock/close without unlocking */
static void test11(void)
{
int rc;
int fd;
int gid;
char buf[10000];
/* Create the test file. */
fd = creat(mainpath, 0);
ASSERTF(fd >= 0, "creat failed for '%s': %s",
mainpath, strerror(errno));
memset(buf, 0x5a, sizeof(buf));
rc = write(fd, buf, sizeof(buf));
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
/* Open/lock and close many times. Open with different
* flags. */
for (gid = 1; gid < 10000; gid++) {
int oflags = O_RDONLY;
switch (gid % 10) {
case 0:
oflags = O_RDONLY;
break;
case 1:
oflags = O_WRONLY;
break;
case 2:
oflags = O_WRONLY | O_APPEND;
break;
case 3:
oflags = O_WRONLY | O_CLOEXEC;
break;
case 4:
oflags = O_WRONLY | O_DIRECT;
break;
case 5:
oflags = O_WRONLY | O_NOATIME;
break;
case 6:
oflags = O_WRONLY | O_SYNC;
break;
case 7:
oflags = O_RDONLY | O_DIRECT;
break;
case 8:
oflags = O_RDWR;
break;
case 9:
oflags = O_RDONLY | O_LOV_DELAY_CREATE;
break;
}
fd = open(mainpath, oflags);
ASSERTF(fd >= 0, "open failed for '%s': %s (oflags=%d, gid=%d)",
mainpath, strerror(errno), oflags, gid);
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s (oflags=%d, gid=%d)",
mainpath, strerror(errno), oflags, gid);
close(fd);
}
}
/* Lock / unlock a volatile file, with different creation flags */
static void test12(void)
{
int rc;
int fd;
int gid;
rc = mkdir(mainpath, 0600);
ASSERTF(rc == 0, "mkdir failed for '%s': %s",
mainpath, strerror(errno));
fd = llapi_create_volatile_idx(mainpath, -1, O_CREAT | O_WRONLY);
ASSERTF(fd >= 0, "llapi_create_volatile_idx failed on '%s': %s",
mainpath, strerror(-fd));
gid = 34895;
rc = llapi_group_lock(fd, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(-rc));
rc = llapi_group_unlock(fd, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(-rc));
close(fd);
fd = llapi_create_volatile_idx(mainpath, -1,
O_CREAT | O_WRONLY | O_LOV_DELAY_CREATE);
ASSERTF(fd >= 0, "llapi_create_volatile_idx failed on '%s': %s",
mainpath, strerror(-fd));
gid = 3354895;
rc = llapi_group_lock(fd, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(-rc));
rc = llapi_group_unlock(fd, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(-rc));
close(fd);
fd = llapi_create_volatile_idx(mainpath, -1, O_RDONLY);
ASSERTF(fd >= 0, "llapi_create_volatile_idx failed on '%s': %s",
mainpath, strerror(-fd));
gid = 3489655;
rc = llapi_group_lock(fd, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(-rc));
rc = llapi_group_unlock(fd, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(-rc));
close(fd);
}
static void helper_test20(int fd)
{
int gid;
int rc;
gid = 1234;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == -1 && (errno == ENOTTY || errno == EINVAL),
"unexpected retval: %d %s", rc, strerror(errno));
gid = 0;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == -1 && (errno == ENOTTY || errno == EINVAL),
"unexpected retval: %d %s", rc, strerror(errno));
gid = 1;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == -1 && (errno == ENOTTY || errno == EINVAL),
"unexpected retval: %d %s", rc, strerror(errno));
gid = -1;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == -1 && (errno == ENOTTY || errno == EINVAL),
"unexpected retval: %d %s", rc, strerror(errno));
}
/* Test lock / unlock on a directory */
static void test20(void)
{
int fd;
int rc;
char dname[PATH_MAX];
/* Try the mountpoint. Should fail. */
fd = open(fsmountdir, O_RDONLY | O_DIRECTORY);
ASSERTF(fd >= 0, "open failed for '%s': %s", mainpath, strerror(errno));
helper_test20(fd);
close(fd);
/* Try .lustre/ . Should fail. */
rc = snprintf(dname, sizeof(dname), "%s/.lustre", fsmountdir);
ASSERTF(rc < sizeof(dname), "Name too long");
fd = open(fsmountdir, O_RDONLY | O_DIRECTORY);
ASSERTF(fd >= 0, "open failed for '%s': %s", mainpath, strerror(errno));
helper_test20(fd);
close(fd);
/* A regular directory. */
rc = mkdir(mainpath, 0600);
ASSERTF(rc == 0, "mkdir failed for '%s': %s",
mainpath, strerror(errno));
fd = open(mainpath, O_RDONLY | O_DIRECTORY);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
helper_test20(fd);
close(fd);
}
/* Test locking between several fds. */
static void test30(void)
{
int fd1;
int fd2;
int gid;
int gid2;
int rc;
/* Create the test file, and open it. */
fd1 = creat(mainpath, 0);
ASSERTF(fd1 >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
/* Open a second time in non blocking mode. */
fd2 = open(mainpath, O_RDWR | O_NONBLOCK);
ASSERTF(fd2 >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
/* Valid command first. */
gid = 1234;
rc = ioctl(fd1, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd1, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
/* Lock on one fd, unlock on the other */
gid = 6947556;
rc = ioctl(fd1, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd2, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected unlock retval: %d %s", rc, strerror(errno));
rc = ioctl(fd1, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
/* Lock from both */
gid = 89489665;
rc = ioctl(fd1, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd2, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd2, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd1, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
/* Lock from both. Unlock in reverse order. */
gid = 89489665;
rc = ioctl(fd1, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd2, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd1, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
rc = ioctl(fd2, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s", mainpath, strerror(errno));
/* Try to lock with different gids */
gid = 89489665;
rc = ioctl(fd1, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s': %s", mainpath, strerror(errno));
for (gid2 = -50; gid2 < 50; gid2++) {
rc = ioctl(fd2, LL_IOC_GROUP_LOCK, gid2);
if (gid2 == 0)
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected lock retval for gid %d: %s",
gid2, strerror(errno));
else
ASSERTF(rc == -1 && errno == EAGAIN,
"unexpected lock retval for gid %d: %s",
gid2, strerror(errno));
}
rc = ioctl(fd1, LL_IOC_GROUP_UNLOCK, gid);
ASSERTF(rc == 0, "cannot unlock '%s': %s",
mainpath, strerror(errno));
close(fd1);
close(fd2);
}
/* Test locking between several fds. */
static void test40(void)
{
int rc;
int fd;
int gid;
char buf[10000];
int i, j, threads = 40;
/* Create the test file. */
fd = open(mainpath, O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
ASSERTF(fd >= 0, "creat failed for '%s': %s",
mainpath, strerror(errno));
/* Lock */
gid = 1234;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
ASSERTF(rc == 0, "cannot lock '%s' gid %d: %s", mainpath, gid,
strerror(errno));
for (i = 0; i < threads; i++) {
rc = fork();
ASSERTF(rc >= 0, "fork failed %s", strerror(errno));
if (rc == 0) {
struct iovec io = { .iov_base = buf,
.iov_len = sizeof(buf)};
/* writing to a fixed offset */
memset(buf, i, sizeof(buf));
rc = pwritev(fd, &io, 1, sizeof(buf) * i);
ASSERTF(rc == sizeof(buf), "write failed for '%s' block %d: %s",
mainpath, i, strerror(errno));
close(fd);
exit(0);
}
}
while ((i = wait(&rc)) > 0);
/* Check data */
for (i = 0; i < threads; i++) {
rc = read(fd, buf, sizeof(buf));
ASSERTF(rc == sizeof(buf), "read failed for '%s': %s",
mainpath, strerror(errno));
for (j = 0; j < rc; j++)
ASSERTF(i == buf[j], "wrong data at off %d %d != %d",
j, i, buf[j]);
}
/* close unlock group lock */
rc = close(fd);
ASSERTF(rc == 0, "close failed '%s': %s",
mainpath, strerror(errno));
}
static void usage(char *prog)
{
fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
exit(EXIT_FAILURE);
}
static void process_args(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "d:")) != -1) {
switch (c) {
case 'd':
lustre_dir = optarg;
break;
case '?':
default:
fprintf(stderr, "Unknown option '%c'\n", optopt);
usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
char fsname[8 + 1];
int rc;
process_args(argc, argv);
if (lustre_dir == NULL)
lustre_dir = "/mnt/lustre";
rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
if (rc != 0) {
fprintf(stderr, "Error: '%s': not a Lustre filesystem\n",
lustre_dir);
return EXIT_FAILURE;
}
/* Play nice with Lustre test scripts. Non-line buffered output
* stream under I/O redirection may appear incorrectly. */
setvbuf(stdout, NULL, _IOLBF, 0);
/* Create a test filename and reuse it. Remove possibly old files. */
rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
ASSERTF(rc > 0 && rc < sizeof(mainpath), "invalid name for mainpath");
cleanup();
atexit(cleanup);
PERFORM(test10);
PERFORM(test11);
PERFORM(test12);
PERFORM(test20);
PERFORM(test30);
PERFORM(test40);
return EXIT_SUCCESS;
}