]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.12-20260715
authorWietse Z Venema <wietse@porcupine.org>
Wed, 15 Jul 2026 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Thu, 16 Jul 2026 17:03:20 +0000 (03:03 +1000)
12 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/src/global/mail_version.h
postfix/src/testing/Makefile.in
postfix/src/testing/mock_fcntl.c [new file with mode: 0644]
postfix/src/testing/mock_fcntl.h [new file with mode: 0644]
postfix/src/util/Makefile.in
postfix/src/util/myflock.c
postfix/src/util/myflock.h
postfix/src/util/myflock_test.c [new file with mode: 0644]
postfix/src/util/wrap_fcntl.c [new file with mode: 0644]
postfix/src/util/wrap_fcntl.h [new file with mode: 0644]

index 81780bfb571d4016bd79d5b2cc92a5b9b0c34555..91d233cad2c5c1f814986c0e8829af23f88748d1 100644 (file)
 -TMOCK_APPL_SIG
 -TMOCK_APPL_STATUS
 -TMOCK_EXPECT
+-TMOCK_FCNTL_REQ
+-TMOCK_FLOCK_REQ
 -TMOCK_OPEN_AS_REQ
 -TMOCK_SERVER
 -TMOCK_SPAWN_CMD_REQ
index 92aa81bcca226a4cd836a29a820bc877a9bc1f0d..7318d3cfcc36dd599c594832cc6c7a76f74a0d3e 100644 (file)
@@ -31717,6 +31717,13 @@ Apologies for any names omitted.
        file descriptor twice (without any adverse effect). Files:
        nbbio.[hc].
 
+20260715
+
+       Code hygiene: myflock() did not reject all invalid requests.
+       Reported by Qualys, assisted by Claude Mythos Preview. Also
+       added proper unit tests. Files: myflock.[hc], myflock_test.c,
+       wrap_fcntl.[hc], mock_fcntl.[hc].
+
 TODO
 
        Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
index 7e43f5231d7256ea10ed9c978216803453e1546c..e553abe85c8bdfa0fbcf35e4e7de6bfdaba189a2 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20260713"
+#define MAIL_RELEASE_DATE      "20260715"
 #define MAIL_VERSION_NUMBER    "3.12"
 
 #ifdef SNAPSHOT
index 3a77e6b34f035cf5f2602b270c26d38dcf7e253c..ca6d6841c8ec6550c2a4b2366cda3c1333d29385 100644 (file)
@@ -3,11 +3,11 @@ SRCS  = mock_myaddrinfo.c mock_dns_lookup.c mock_servent.c \
        mock_getaddrinfo.c match_basic.c match_addr.c make_addr.c \
        addrinfo_to_string.c make_attr.c match_attr.c \
        nosleep.c dict_test_helper.c mock_open_as.c mock_spawn_command.c \
-       mock_stat.c msg_capture.c mock_dict.c
+       mock_stat.c msg_capture.c mock_dict.c mock_fcntl.c
 LIB_OBJ        = match_basic.o match_addr.o make_addr.o addrinfo_to_string.o \
        make_attr.o match_attr.o \
        dict_test_helper.o mock_open_as.o mock_spawn_command.o \
-       mock_stat.o msg_capture.o mock_dict.o
+       mock_stat.o msg_capture.o mock_dict.o mock_fcntl.o
 MOCK_OBJ= mock_myaddrinfo.o mock_dns_lookup.o mock_servent.o \
        mock_getaddrinfo.o mock_server.o
 TEST_OBJ = mock_dns_lookup_test.o mock_getaddrinfo_test.o \
@@ -17,7 +17,7 @@ HDRS  = mock_myaddrinfo.h mock_dns.h mock_servent.h mock_getaddrinfo.h \
        match_basic.h match_addr.h make_addr.h addrinfo_to_string.h \
        mock_server.h make_attr.h match_attr.h \
        dict_test_helper.h mock_open_as.h mock_spawn_command.h \
-       mock_stat.h msg_capture.h mock_dict.h
+       mock_stat.h msg_capture.h mock_dict.h mock_fcntl.h
 TESTSRC        =
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -340,6 +340,18 @@ mock_dns_lookup_test.o: ../../include/vstream.h
 mock_dns_lookup_test.o: ../../include/vstring.h
 mock_dns_lookup_test.o: mock_dns.h
 mock_dns_lookup_test.o: mock_dns_lookup_test.c
+mock_fcntl.o: ../../include/argv.h
+mock_fcntl.o: ../../include/binhash.h
+mock_fcntl.o: ../../include/check_arg.h
+mock_fcntl.o: ../../include/msg.h
+mock_fcntl.o: ../../include/msg_jmp.h
+mock_fcntl.o: ../../include/ptest.h
+mock_fcntl.o: ../../include/sys_defs.h
+mock_fcntl.o: ../../include/vbuf.h
+mock_fcntl.o: ../../include/vstream.h
+mock_fcntl.o: ../../include/vstring.h
+mock_fcntl.o: mock_fcntl.c
+mock_fcntl.o: mock_fcntl.h
 mock_getaddrinfo.o: ../../include/argv.h
 mock_getaddrinfo.o: ../../include/check_arg.h
 mock_getaddrinfo.o: ../../include/msg.h
diff --git a/postfix/src/testing/mock_fcntl.c b/postfix/src/testing/mock_fcntl.c
new file mode 100644 (file)
index 0000000..dab67cc
--- /dev/null
@@ -0,0 +1,228 @@
+/*++
+/* 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 */
diff --git a/postfix/src/testing/mock_fcntl.h b/postfix/src/testing/mock_fcntl.h
new file mode 100644 (file)
index 0000000..b7dc4ec
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _MOCK_FCNTL_H_INCLUDED_
+#define _MOCK_FCNTL_H_INCLUDED_
+
+/*++
+/* NAME
+/*     mock_fcntl 3h
+/* SUMMARY
+/*     mock fcntl/flock
+/* SYNOPSIS
+/*     #include <mock_fcntl.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <fcntl.h>
+#include <unistd.h>
+#include <stddef.h>                    /* offsetof() */
+
+ /*
+  * External interface.
+  */
+typedef struct MOCK_FCNTL_REQ {
+    /* Inputs. */
+    int     want_fd;
+    int     want_cmd;
+    union {
+       struct flock fl;
+       int     fd;
+       int     flags;
+    }       want_arg;
+    /* Outputs. */
+    int     out_ret;
+    int     out_errno;
+} MOCK_FCNTL_REQ;
+
+#define MOCK_FCNTL_REQ_WANT_SIZE offsetof(MOCK_FCNTL_REQ, out_ret)
+
+extern void setup_mock_fcntl(const MOCK_FCNTL_REQ *);
+extern void teardown_mock_fcntl(void);
+
+typedef struct MOCK_FLOCK_REQ {
+    /* Inputs. */
+    int     want_fd;
+    int     want_cmd;
+    /* Outputs. */
+    int     out_ret;
+    int     out_errno;
+} MOCK_FLOCK_REQ;
+
+#define MOCK_FLOCK_REQ_WANT_SIZE offsetof(MOCK_FLOCK_REQ, out_ret)
+
+extern void setup_mock_flock(const MOCK_FLOCK_REQ *);
+extern void teardown_mock_flock(void);
+
+/* LICENSE
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+#endif
index 754dff0bc4163a4896b1bdd1421141393ac59747..2f9a639186635a64c1a588ca92f8bcfcb4cbc0ff 100644 (file)
@@ -48,7 +48,8 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        inet_addr_sizes.c quote_for_json.c mystrerror.c \
        sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.c \
        clean_ascii_cntrl_space.c normalize_v4mapped_addr.c ossl_digest.c \
-       mac_midna.c wrap_stat.c dynamicmaps.c find_inet_service.c wrap_netdb.c
+       mac_midna.c wrap_stat.c dynamicmaps.c find_inet_service.c wrap_netdb.c \
+       wrap_fcntl.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -98,7 +99,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
        normalize_ws.o valid_uri_scheme.o clean_ascii_cntrl_space.o \
        normalize_v4mapped_addr.o ossl_digest.o mac_midna.o wrap_stat.o \
-       dynamicmaps.o find_inet_service.o wrap_netdb.o
+       dynamicmaps.o find_inet_service.o wrap_netdb.o wrap_fcntl.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -133,13 +134,14 @@ HDRS      = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \
        inet_prefix_top.h inet_addr_sizes.h valid_uri_scheme.h \
        clean_ascii_cntrl_space.h normalize_v4mapped_addr.h ossl_digest.h \
-       mac_midna.h wrap_stat.h dynamicmaps.h find_inet_service.h wrap_netdb.h
+       mac_midna.h wrap_stat.h dynamicmaps.h find_inet_service.h wrap_netdb.h \
+       wrap_fcntl.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        sunos5_stream_test.c dup2_pass_on_exec.c argv_test.c dict_pipe_test.c \
        dict_stream_test.c dict_cli.c dict_union_test.c \
        find_inet_service_test.c hash_fnv_test.c known_tcp_ports_test.c \
        msg_output_test.c myaddrinfo_test.c mymalloc_test.c mystrtok_test.c \
-       unescape_test.c allprint_test.c
+       unescape_test.c allprint_test.c myflock_test.c
 DEFS   = -I. -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 FILES  = Makefile $(SRCS) $(HDRS)
@@ -163,7 +165,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        dict_union_test dict_pipe_test myaddrinfo_test \
        clean_env inet_prefix_top printable readlline quote_for_json \
        normalize_ws valid_uri_scheme clean_ascii_cntrl_space \
-       normalize_v4mapped_addr_test ossl_digest_test allprint_test
+       normalize_v4mapped_addr_test ossl_digest_test allprint_test \
+       myflock_test
 PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
        $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX) \
        $(LIB_PREFIX)db$(LIB_SUFFIX)
@@ -465,6 +468,9 @@ hash_fnv_test: $(LIB) $(TESTLIBS) update
 allprint_test: $(LIB) $(TESTLIBS) update
        $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(TESTLIBS) $(SYSLIBS)
 
+myflock_test: $(LIB) $(TESTLIBS) update
+       $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(TESTLIBS) $(SYSLIBS)
+
 unix_recv_fd:  $(LIB)
        mv $@.o junk
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
@@ -843,6 +849,9 @@ test_hash_fnv: hash_fnv_test
 test_allprint: allprint_test
        $(SHLIB_ENV) ${VALGRIND} ./allprint_test
 
+test_myflock: myflock_test
+       $(SHLIB_ENV) ${VALGRIND} ./myflock_test
+
 hex_code_test: hex_code
        $(SHLIB_ENV) ${VALGRIND} ./hex_code
 
@@ -2750,7 +2759,7 @@ myflock.o: check_arg.h
 myflock.o: msg.h
 myflock.o: myflock.c
 myflock.o: myflock.h
-myflock.o: name_mask.h
+myflock.o: name_code.h
 myflock.o: sys_defs.h
 myflock.o: vbuf.h
 myflock.o: vstring.h
@@ -3387,6 +3396,10 @@ watchdog.o: posix_signals.h
 watchdog.o: sys_defs.h
 watchdog.o: watchdog.c
 watchdog.o: watchdog.h
+wrap_fcntl.o: msg.h
+wrap_fcntl.o: sys_defs.h
+wrap_fcntl.o: wrap_fcntl.c
+wrap_fcntl.o: wrap_fcntl.h
 wrap_netdb.o: sys_defs.h
 wrap_netdb.o: wrap_netdb.c
 wrap_netdb.o: wrap_netdb.h
index 2c8ad5d8ab0103890428197645a3c5e5db728176..7a2b3f774ef70fa31259b812cf0f8dd0fc12a819 100644 (file)
@@ -60,6 +60,9 @@
 /*     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;
        }
@@ -146,19 +143,15 @@ int     myflock(int fd, int lock_style, int operation)
         * 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;
        }
@@ -173,7 +166,7 @@ int     myflock(int fd, int lock_style, int operation)
      * 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;
 
index ee18bdba0f107c8f66c9775fe0e2c93fa364d80f..32cf78682c8eaca5528396c10473206914deb675 100644 (file)
@@ -30,9 +30,6 @@ extern int WARN_UNUSED_RESULT myflock(int, int, int);
 #define MYFLOCK_OP_EXCLUSIVE   2
 #define MYFLOCK_OP_NOWAIT      4
 
-#define MYFLOCK_OP_BITS \
-       (MYFLOCK_OP_SHARED | MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT)
-
 /* LICENSE
 /* .ad
 /* .fi
@@ -47,6 +44,9 @@ extern int WARN_UNUSED_RESULT myflock(int, int, int);
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 #endif
diff --git a/postfix/src/util/myflock_test.c b/postfix/src/util/myflock_test.c
new file mode 100644 (file)
index 0000000..51cf72d
--- /dev/null
@@ -0,0 +1,439 @@
+ /*
+  * 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>
diff --git a/postfix/src/util/wrap_fcntl.c b/postfix/src/util/wrap_fcntl.c
new file mode 100644 (file)
index 0000000..dec0e48
--- /dev/null
@@ -0,0 +1,105 @@
+/*++
+/* NAME
+/*     wrap_fcntl 3
+/* SUMMARY
+/*     mockable fcntl/flock wrappers
+/* SYNOPSIS
+/*     #include <wrap_fcntl.h>
+/*
+/*     int     wrap_fcntl(
+/*     int     fd,
+/*     int     cmd,
+/*     ...)
+/*
+/*     int     wrap_fcntl(
+/*     int     fd,
+/*     int     cmd)
+/* DESCRIPTION
+/*     This module is a NOOP when the NO_MOCK_WRAPPERS macro is defined.
+/*
+/*     By default this module redirects fcntl() and flock() calls
+/*     to the above listed wrapper functions that may be overridden
+/*     with mocks. This arrangement prevents mock fcntl() or flock()
+/*     functions from interfering with fcntl() or flock() calls made
+/*     by system libraries or by third-party libraries.
+/* 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/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <wrap_fcntl.h>
+
+#ifdef HAS_FCNTL_LOCK
+
+/* wrap_fcntl - mockable wrapper */
+
+int     wrap_fcntl(int fd, int cmd,...)
+{
+    va_list ap;
+    int     ret;
+
+#undef fcntl
+
+    va_start(ap, cmd);
+    switch (cmd) {
+
+    case F_GETFD:
+    case F_GETFL:
+       /* Operations with no third argument. */
+       ret = fcntl(fd, cmd);
+       break;
+
+    case F_DUPFD:
+    case F_SETFD:
+    case F_SETFL:
+       /* Operations with an integer third argument. */
+       ret = fcntl(fd, cmd, va_arg(ap, int));
+       break;
+
+    case F_SETLK:
+    case F_SETLKW:
+    case F_GETLK:
+       /* Lock operations. */
+       ret = fcntl(fd, cmd, va_arg(ap, struct flock *));
+       break;
+
+    default:
+       /* Platform-specific, should never be called. */
+       msg_warn("%s: unexpected cmd=%d", __func__, cmd);
+       ret = -1;
+       errno = ENOTSUP;
+       break;
+    }
+    va_end(ap);
+    return (ret);
+}
+
+#endif                                 /* HAS_FCNTL_LOCK */
+
+#ifdef HAS_FLOCK_LOCK
+
+/* wrap_flock - mockable wrapper */
+
+int     wrap_flock(int fd, int cmd)
+{
+#undef flock
+    return (flock(fd, cmd));
+}
+
+#endif                                 /* HAS_FLOCK_LOCK */
diff --git a/postfix/src/util/wrap_fcntl.h b/postfix/src/util/wrap_fcntl.h
new file mode 100644 (file)
index 0000000..c2622d3
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _WRAP_FCNTL_H_INCLUDED_
+#define _WRAP_FCNTL_H_INCLUDED_
+
+/*++
+/* NAME
+/*     wrap_fcntl 3h
+/* SUMMARY
+/*     mockable fcntl/flock wrappers
+/* SYNOPSIS
+/*     #include <wrap_fcntl.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <sys/file.h>
+#include <fcntl.h>
+#include <stdarg.h>
+
+ /*
+  * Mockable API.
+  */
+#ifndef NO_MOCK_WRAPPERS
+extern int wrap_fcntl(int, int,...);
+extern int wrap_flock(int, int);
+
+#define fcntl(fd, cmd, ...)    wrap_fcntl(fd, cmd, __VA_ARGS__)
+#define flock(fd, cmd)         wrap_flock(fd, cmd)
+#endif
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+#endif