--- /dev/null
+/*++
+/* NAME
+/* mock_fcntl 3
+/* SUMMARY
+/* mock fcntl/flock
+/* SYNOPSIS
+/* #include <mock_fcntl.h>
+/*
+/* typedef struct MOCK_FCNTL_REQ {
+/* .in +4
+/* int want_fd;
+/* int want_cmd;
+/* union {
+/* .in +4
+/* struct flock fl;
+/* int flags;
+/* .in -4
+/* } want_arg;
+/* int out_ret;
+/* int out_errno;
+/* .in -4
+/* } MOCK_FCNTL_REQ;
+/*
+/* void setup_mock_fcntl(const MOCK_FCNTL_REQ *request)
+/*
+/* void teardown_mock_fcntl(void)
+/*
+/* typedef struct MOCK_FLOCK_REQ {
+/* .in +4
+/* int want_fd;
+/* int want_cmd;
+/* .in -4
+/* int out_ret;
+/* int out_errno;
+/* } MOCK_FLOCK_REQ;
+/*
+/* void setup_mock_flock(const MOCK_FLOCK_REQ *request)
+/*
+/* void teardown_mock_flock(void)
+/* DESCRIPTION
+/* This module provides mock fcntl() and flock() implementations
+/* that return static results for expected calls.
+/*
+/* setup_mock_fcntl() adds a mapping from fcntl() requests to
+/* fcntl() results. It is an error to enter multiple mappings for
+/* the same request. setup_mock_fcntl() makes no copy of its input:
+/* .IP want_fd
+/* .IP want_cmd
+/* .IP want_arg
+/* These form the lookup key for the setup_mock_fcntl() mapping.
+/* The caller must zero-fill unused space in and around the want_xxx
+/* request members.
+/* .IP out_ret
+/* The mock fcntl() result value. If -1, then out_errno must be
+/* non-zero.
+/* .IP out_errno
+/* The global out_errno upon return from mock fcntl(). If this is
+/* non-zero, mock fcntl() must return -1.
+/* .PP
+/* teardown_mock_fcntl() destroys all mappings entered with
+/* setup_mock_fcntl().
+/*
+/* The mock fcntl() function looks up the MOCK_FCNTL_REQ information
+/* for an fcntl() request, and terminates the test if the request
+/* is not expected.
+/*
+/* setup_mock_flock() and teardown_mock_flock() implement similar
+/* functionality for flock() calls.
+/* SEE ALSO
+/* fcntl(2), flock(2), the real things
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdarg.h>
+
+ /*
+ * Utility library.
+ */
+#include <binhash.h>
+#include <wrap_fcntl.h>
+#include <msg.h>
+#include <string.h>
+
+ /*
+ * Test library.
+ */
+#include <ptest.h>
+#include <mock_fcntl.h>
+
+ /*
+ * Application-specific.
+ */
+static BINHASH *mock_fcntl_table;
+static BINHASH *mock_flock_table;
+
+#ifdef HAS_FCNTL_LOCK
+
+/* setup_mock_fcntl - add request-to-result mapping */
+
+void setup_mock_fcntl(const MOCK_FCNTL_REQ *request)
+{
+ if (mock_fcntl_table == 0)
+ mock_fcntl_table = binhash_create(10);
+ if (request->out_ret == -1 && request->out_errno == 0)
+ ptest_fatal(ptest_ctx_current(),
+ "setup_mock_fcntl: result -1 requires non-zero errno");
+ if (request->out_ret != -1 && request->out_errno != 0)
+ ptest_fatal(ptest_ctx_current(),
+ "setup_mock_fcntl: result %d requires zero errno",
+ request->out_ret);
+ binhash_enter(mock_fcntl_table, (void *) request,
+ MOCK_FCNTL_REQ_WANT_SIZE, (void *) request);
+}
+
+/* teardown_mock_fcntl - destroy all request-to-result mappings */
+
+void teardown_mock_fcntl(void)
+{
+ if (mock_fcntl_table) {
+ binhash_free(mock_fcntl_table, (void (*) (void *)) 0);
+ mock_fcntl_table = 0;
+ }
+}
+
+/* fcntl - mock fcntl() implementation */
+
+int fcntl(int fd, int cmd,...)
+{
+ MOCK_FCNTL_REQ mock_key;
+ MOCK_FCNTL_REQ *mock_info;
+ va_list ap;
+
+ if (mock_fcntl_table != 0) {
+ va_start(ap, cmd);
+ memset((void *) &mock_key, 0, MOCK_FCNTL_REQ_WANT_SIZE);
+ mock_key.want_fd = fd;
+ mock_key.want_cmd = cmd;
+ switch (cmd) {
+ case F_GETFD:
+ case F_GETFL:
+ break;
+ case F_DUPFD:
+ case F_SETFD:
+ case F_SETFL:
+ mock_key.want_arg.fd = va_arg(ap, int);
+ break;
+ case F_SETLK:
+ case F_SETLKW:
+ case F_GETLK:
+ mock_key.want_arg.fl = *va_arg(ap, struct flock *);
+ break;
+ }
+ va_end(ap);
+ }
+ if (mock_fcntl_table == 0
+ || (mock_info = (MOCK_FCNTL_REQ *)
+ binhash_find(mock_fcntl_table, &mock_key,
+ MOCK_FCNTL_REQ_WANT_SIZE)) == 0)
+ ptest_fatal(ptest_ctx_current(),
+ "unexpected request: fcntl(%d, %d, ...)", fd, cmd);
+ errno = mock_info->out_errno;
+ return (mock_info->out_ret);
+}
+
+#endif /* HAS_FCNTL_LOCK */
+
+#ifdef HAS_FLOCK_LOCK
+
+/* setup_mock_flock - add request-to-result mapping */
+
+void setup_mock_flock(const MOCK_FLOCK_REQ *request)
+{
+ if (mock_flock_table == 0)
+ mock_flock_table = binhash_create(10);
+ if (request->out_ret == -1 && request->out_errno == 0)
+ ptest_fatal(ptest_ctx_current(),
+ "setup_mock_flock: result -1 requires non-zero errno");
+ if (request->out_ret != -1 && request->out_errno != 0)
+ ptest_fatal(ptest_ctx_current(),
+ "setup_mock_flock: result %d requires zero errno",
+ request->out_ret);
+ binhash_enter(mock_flock_table, (void *) request,
+ MOCK_FLOCK_REQ_WANT_SIZE, (void *) request);
+}
+
+/* teardown_mock_flock - destroy all request-to-result mappings */
+
+void teardown_mock_flock(void)
+{
+ if (mock_flock_table) {
+ binhash_free(mock_flock_table, (void (*) (void *)) 0);
+ mock_flock_table = 0;
+ }
+}
+
+/* flock - mock flock() implementation */
+
+int flock(int fd, int cmd)
+{
+ MOCK_FLOCK_REQ mock_key;
+ MOCK_FLOCK_REQ *mock_info;
+
+ mock_key.want_fd = fd;
+ mock_key.want_cmd = cmd;
+ if (mock_flock_table == 0
+ || (mock_info = (MOCK_FLOCK_REQ *)
+ binhash_find(mock_flock_table, &mock_key,
+ MOCK_FLOCK_REQ_WANT_SIZE)) == 0)
+ ptest_fatal(ptest_ctx_current(),
+ "unexpected request: flock(%d, %d)", fd, cmd);
+ errno = mock_info->out_errno;
+ return (mock_info->out_ret);
+}
+
+#endif /* HAS_FLOCK_LOCK */
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
#include "msg.h"
#include "myflock.h"
-#include "name_mask.h"
+#include "name_code.h"
#include "vstring.h"
+#include <wrap_fcntl.h>
/* myflock - lock/unlock entire open file */
int myflock(int fd, int lock_style, int operation)
{
int status;
- const static NAME_MASK lock_masks[] = {
+ const static NAME_CODE lock_style_ux[] = {
"MYFLOCK_STYLE_FLOCK", MYFLOCK_STYLE_FLOCK,
"MYFLOCK_STYLE_FCNTL", MYFLOCK_STYLE_FCNTL,
0,
};
- const static NAME_MASK op_masks[] = {
+ const static NAME_CODE lock_req_ux[] = {
+ "MYFLOCK_OP_NONE", MYFLOCK_OP_NONE,
"MYFLOCK_OP_SHARED", MYFLOCK_OP_SHARED,
"MYFLOCK_OP_EXCLUSIVE", MYFLOCK_OP_EXCLUSIVE,
- "MYFLOCK_OP_NOWAIT", MYFLOCK_OP_NOWAIT,
0,
};
-
- if (msg_verbose) {
- VSTRING *style_buf = vstring_alloc(100);
- VSTRING *op_buf = vstring_alloc(100);
-
- msg_info("myflock(%d, %s, %s)", fd,
- str_name_mask_opt(style_buf, "lock_style", lock_masks,
- lock_style, NAME_MASK_PIPE | NAME_MASK_NUMBER),
- operation == MYFLOCK_OP_NONE ? "MYFLOCK_OP_NONE" :
- str_name_mask_opt(op_buf, "operation", op_masks,
- operation, NAME_MASK_PIPE | NAME_MASK_NUMBER));
- vstring_free(style_buf);
- vstring_free(op_buf);
- }
+ int nowait_req = (operation & MYFLOCK_OP_NOWAIT);
+ int lock_req = (operation & ~MYFLOCK_OP_NOWAIT);
/*
* Sanity check.
*/
- if ((operation & (MYFLOCK_OP_BITS)) != operation)
+ if (lock_style < MYFLOCK_STYLE_FLOCK || lock_style > MYFLOCK_STYLE_FCNTL)
+ msg_panic("myflock: unsupported lock style: 0x%x", lock_style);
+ if (lock_req < MYFLOCK_OP_NONE || lock_req > MYFLOCK_OP_EXCLUSIVE)
msg_panic("myflock: improper operation type: 0x%x", operation);
+ if (msg_verbose) {
+ msg_info("myflock(%d, %s, %s%s)", fd,
+ str_name_code(lock_style_ux, lock_style),
+ str_name_code(lock_req_ux, lock_req),
+ nowait_req ? " | MYFLOCK_OP_NOWAIT" : "");
+ }
switch (lock_style) {
/*
* flock() does exactly what we need. Too bad it is not standard.
*/
#ifdef HAS_FLOCK_LOCK
- case MYFLOCK_STYLE_FLOCK:
- {
- static int lock_ops[] = {
- LOCK_UN, LOCK_SH, LOCK_EX, -1,
- -1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1
- };
-
- while ((status = flock(fd, lock_ops[operation])) < 0
- && errno == EINTR)
+ case MYFLOCK_STYLE_FLOCK:{
+ static int flock_reqs[] = {LOCK_UN, LOCK_SH, LOCK_EX,};
+ int flock_req = flock_reqs[lock_req]
+ | (nowait_req ? LOCK_NB : 0);
+
+ while ((status = flock(fd, flock_req)) < 0 && errno == EINTR)
sleep(1);
break;
}
* it.
*/
#ifdef HAS_FCNTL_LOCK
- case MYFLOCK_STYLE_FCNTL:
- {
+ case MYFLOCK_STYLE_FCNTL:{
struct flock lock;
- int request;
- static int lock_ops[] = {
- F_UNLCK, F_RDLCK, F_WRLCK
- };
+ static int fcntl_lock_reqs[] = {F_UNLCK, F_RDLCK, F_WRLCK,};
+ int fcntl_req = (nowait_req ? F_SETLK : F_SETLKW);
memset((void *) &lock, 0, sizeof(lock));
- lock.l_type = lock_ops[operation & ~MYFLOCK_OP_NOWAIT];
- request = (operation & MYFLOCK_OP_NOWAIT) ? F_SETLK : F_SETLKW;
- while ((status = fcntl(fd, request, &lock)) < 0
- && errno == EINTR)
+ lock.l_type = fcntl_lock_reqs[lock_req];
+
+ while ((status = fcntl(fd, fcntl_req, &lock)) < 0 && errno == EINTR)
sleep(1);
break;
}
* Return a consistent result. Some systems return EACCES when a lock is
* taken by someone else, and that would complicate error processing.
*/
- if (status < 0 && (operation & MYFLOCK_OP_NOWAIT) != 0)
+ if (status < 0 && nowait_req)
if (errno == EWOULDBLOCK || errno == EACCES)
errno = EAGAIN;
--- /dev/null
+ /*
+ * Test program to verify that myflock() makes the expected flock() and
+ * fcntl() calls.
+ */
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <errno.h>
+#include <string.h>
+
+ /*
+ * Utility library.
+ */
+#include <wrap_fcntl.h>
+#include <myflock.h>
+
+ /*
+ * Test library.
+ */
+#include <ptest.h>
+#include <mock_fcntl.h>
+
+typedef struct PTEST_CASE {
+ const char *testname; /* identifies test case */
+ void (*action) (PTEST_CTX *t,
+ const struct PTEST_CASE *tp);
+} PTEST_CASE;
+
+static void test_myflock_fcntl_shared_nb(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FCNTL_REQ lock_req;
+ MOCK_FCNTL_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = F_SETLK;
+ lock_req.want_arg.fl.l_type = F_RDLCK;
+ setup_mock_fcntl(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL,
+ MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_SHARED|MYFLOCK_OP_NOWAIT): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = F_SETLKW;
+ unlock_req.want_arg.fl.l_type = F_UNLCK;
+ setup_mock_fcntl(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_fcntl();
+}
+
+static void test_myflock_fcntl_shared_blk(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FCNTL_REQ lock_req;
+ MOCK_FCNTL_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = F_SETLKW;
+ lock_req.want_arg.fl.l_type = F_RDLCK;
+ setup_mock_fcntl(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_SHARED);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_SHARED): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = F_SETLKW;
+ unlock_req.want_arg.fl.l_type = F_UNLCK;
+ setup_mock_fcntl(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_fcntl();
+}
+
+static void test_myflock_fcntl_excl_nb(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FCNTL_REQ lock_req;
+ MOCK_FCNTL_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = F_SETLK;
+ lock_req.want_arg.fl.l_type = F_WRLCK;
+ setup_mock_fcntl(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL,
+ MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_EXCLUSIVE|MYFLOCK_OP_NOWAIT): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = F_SETLKW;
+ unlock_req.want_arg.fl.l_type = F_UNLCK;
+ setup_mock_fcntl(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+}
+
+static void test_myflock_fcntl_excl_blk(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FCNTL_REQ lock_req;
+ MOCK_FCNTL_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = F_SETLKW;
+ lock_req.want_arg.fl.l_type = F_WRLCK;
+ setup_mock_fcntl(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_EXCLUSIVE);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_EXCLUSIVE): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = F_SETLKW;
+ unlock_req.want_arg.fl.l_type = F_UNLCK;
+ setup_mock_fcntl(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FCNTL, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_fcntl();
+}
+
+static void test_myflock_flock_shared_nb(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FLOCK_REQ lock_req;
+ MOCK_FLOCK_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = LOCK_SH | LOCK_NB;
+ setup_mock_flock(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK,
+ MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_SHARED"
+ "|MYFLOCK_OP_NOWAIT): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = LOCK_UN;
+ setup_mock_flock(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_flock();
+}
+
+static void test_myflock_flock_shared_blk(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FLOCK_REQ lock_req;
+ MOCK_FLOCK_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = LOCK_SH;
+ setup_mock_flock(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_SHARED);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_SHARED): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = LOCK_UN;
+ setup_mock_flock(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_flock();
+}
+
+static void test_myflock_flock_excl_nb(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FLOCK_REQ lock_req;
+ MOCK_FLOCK_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = LOCK_EX | LOCK_NB;
+ setup_mock_flock(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK,
+ MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_EXCLUSIVE"
+ "|MYFLOCK_OP_NOWAIT): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = LOCK_UN;
+ setup_mock_flock(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_flock();
+}
+
+static void test_myflock_flock_excl_blk(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FLOCK_REQ lock_req;
+ MOCK_FLOCK_REQ unlock_req;
+ int ret;
+
+ ptest_info(t, "acquire lock");
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = 0;
+ lock_req.want_cmd = LOCK_EX;
+ setup_mock_flock(&lock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_EXCLUSIVE);
+ if (ret != lock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_EXCLUSIVE): got %d, want %d",
+ ret, lock_req.out_ret);
+
+ ptest_info(t, "release lock");
+ memset((void *) &unlock_req, 0, sizeof(lock_req));
+ unlock_req.want_fd = 0;
+ unlock_req.want_cmd = LOCK_UN;
+ setup_mock_flock(&unlock_req);
+ ret = myflock(0, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_NONE);
+ if (ret != unlock_req.out_ret)
+ ptest_fatal(t, "myflock(fd, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_NONE): got %d, want %d",
+ ret, unlock_req.out_ret);
+
+ teardown_mock_flock();
+}
+
+static void test_myflock_bad_style_0(PTEST_CTX *t, const struct PTEST_CASE *tp)
+{
+ expect_ptest_log_event(t, "panic: myflock: unsupported lock style: 0x0");
+ switch (myflock(0, MYFLOCK_STYLE_FLOCK - 1, -1)) {
+ };
+}
+
+static void test_myflock_bad_style_3(PTEST_CTX *t, const struct PTEST_CASE *tp)
+{
+ expect_ptest_log_event(t, "panic: myflock: unsupported lock style: 0x3");
+ switch (myflock(0, MYFLOCK_STYLE_FCNTL + 1, -1)) {
+ };
+}
+
+static void test_myflock_bad_operation_1(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ expect_ptest_log_event(t, "panic: myflock: improper operation type: 0xffffffff");
+ switch (myflock(0, MYFLOCK_STYLE_FCNTL, -1)) {
+ };
+}
+
+static void test_myflock_bad_operation_3(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ expect_ptest_log_event(t, "panic: myflock: improper operation type: 0x3");
+ switch (myflock(0, MYFLOCK_STYLE_FCNTL, 3)) {
+ };
+}
+
+static void test_myflock_propagates_fcntl_error(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FCNTL_REQ lock_req;
+ int fd = -1;
+ int ret;
+
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = fd;
+ lock_req.want_cmd = F_SETLKW;
+ lock_req.want_arg.fl.l_type = F_WRLCK;
+ setup_mock_fcntl(&lock_req);
+ lock_req.out_ret = -1;
+ lock_req.out_errno = EBADF;
+ setup_mock_fcntl(&lock_req);
+ ret = myflock(fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_EXCLUSIVE);
+ if (ret != lock_req.out_ret)
+ ptest_error(t, "myflock(-1, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_EXCLUSIVE): got %d, want %d",
+ ret, lock_req.out_ret);
+ if (errno != lock_req.out_errno)
+ ptest_error(t, "errno: got %d, want %d", errno, lock_req.out_errno);
+
+ teardown_mock_fcntl();
+}
+
+static void test_myflock_propagates_flock_error(PTEST_CTX *t,
+ const struct PTEST_CASE *tp)
+{
+ MOCK_FLOCK_REQ lock_req;
+ int fd = -1;
+ int ret;
+
+ memset((void *) &lock_req, 0, sizeof(lock_req));
+ lock_req.want_fd = fd;
+ lock_req.want_cmd = LOCK_EX;
+ lock_req.out_ret = -1;
+ lock_req.out_errno = EBADF;
+ setup_mock_flock(&lock_req);
+ ret = myflock(fd, MYFLOCK_STYLE_FLOCK, MYFLOCK_OP_EXCLUSIVE);
+ if (ret != lock_req.out_ret)
+ ptest_error(t, "myflock(-1, MYFLOCK_STYLE_FLOCK, "
+ "MYFLOCK_OP_EXCLUSIVE): got %d, want %d",
+ ret, lock_req.out_ret);
+ if (errno != lock_req.out_errno)
+ ptest_error(t, "errno: got %d, want %d", errno, lock_req.out_errno);
+
+ teardown_mock_flock();
+}
+
+ /*
+ * The test cases.
+ */
+static const PTEST_CASE ptestcases[] = {
+ {
+ .testname = "fcntl style, shared, non-blocking",
+ .action = test_myflock_fcntl_shared_nb,
+ },
+ {
+ .testname = "fcntl style, shared, blocking",
+ .action = test_myflock_fcntl_shared_blk,
+ },
+ {
+ .testname = "fcntl style, exclusive, non-blocking",
+ .action = test_myflock_fcntl_excl_nb,
+ },
+ {
+ .testname = "fcntl style, exclusive, blocking",
+ .action = test_myflock_fcntl_excl_blk,
+ },
+ {
+ .testname = "flock style, shared, non-blocking",
+ .action = test_myflock_flock_shared_nb,
+ },
+ {
+ .testname = "flock style, shared, blocking",
+ .action = test_myflock_flock_shared_blk,
+ },
+ {
+ .testname = "flock style, exclusive, non-blocking",
+ .action = test_myflock_flock_excl_nb,
+ },
+ {
+ .testname = "flock style, exclusive, blocking",
+ .action = test_myflock_flock_excl_blk,
+ },
+ {
+ .testname = "detects bad lock style 0",
+ .action = test_myflock_bad_style_0,
+ },
+ {
+ .testname = "detects bad lock style 3",
+ .action = test_myflock_bad_style_3,
+ },
+ {
+ .testname = "detects bad lock operation -1",
+ .action = test_myflock_bad_operation_1,
+ },
+ {
+ .testname = "detects bad lock operation 3",
+ .action = test_myflock_bad_operation_3,
+ },
+ {
+ .testname = "propagates fcntl error",
+ .action = test_myflock_propagates_fcntl_error,
+ },
+ {
+ .testname = "propagates flock error",
+ .action = test_myflock_propagates_flock_error,
+ },
+};
+
+#include <ptest_main.h>