aboutsummaryrefslogtreecommitdiff
path: root/flock_mmap
diff options
context:
space:
mode:
authorDavid Timber <dxdt@dev.snart.me>2024-03-22 10:58:42 +0900
committerDavid Timber <dxdt@dev.snart.me>2024-03-22 11:06:45 +0900
commit5fc02e2e847608a67d46af04d5f935cd889b23c3 (patch)
treebace61b3c4836a665e004562f48f441ee359d1c2 /flock_mmap
parent567496b2b4e68eafb0bc37b7e3ed5acf76afb3a6 (diff)
Add flock_mmap
Diffstat (limited to 'flock_mmap')
-rw-r--r--flock_mmap/.gitignore2
-rw-r--r--flock_mmap/Makefile15
-rw-r--r--flock_mmap/README.md50
-rw-r--r--flock_mmap/flock_mmap.c118
-rwxr-xr-xflock_mmap/tests.sh54
5 files changed, 239 insertions, 0 deletions
diff --git a/flock_mmap/.gitignore b/flock_mmap/.gitignore
new file mode 100644
index 0000000..052f66c
--- /dev/null
+++ b/flock_mmap/.gitignore
@@ -0,0 +1,2 @@
+flock_mmap
+lock
diff --git a/flock_mmap/Makefile b/flock_mmap/Makefile
new file mode 100644
index 0000000..0275f91
--- /dev/null
+++ b/flock_mmap/Makefile
@@ -0,0 +1,15 @@
+CC := cc
+OBJ = flock_mmap lock
+
+do: build test
+
+clean:
+ rm -f $(OBJ)
+
+flock_mmap: flock_mmap.c
+ $(CC) -std=c11 -Wall -Wextra -g -O0 -o flock_mmap flock_mmap.c
+
+build: flock_mmap
+
+test: build
+ ./tests.sh
diff --git a/flock_mmap/README.md b/flock_mmap/README.md
new file mode 100644
index 0000000..623fd5c
--- /dev/null
+++ b/flock_mmap/README.md
@@ -0,0 +1,50 @@
+# Mixing flock() and close() on Various Unices
+https://stackoverflow.com/questions/70045539/file-retains-lock-after-mmap-and-close
+
+This turned out to be a bigger problem than I thought. Well, too late to fix the
+kernel now.
+
+Added `fcntl()` as control.
+
+## INSTALL
+```sh
+mkdir flock_mmap
+cd flock_mmap
+
+wget \
+ https://github.com/dxdxdt/gists/raw/master/flock_mmap/tests.sh \
+ https://github.com/dxdxdt/gists/raw/master/flock_mmap/Makefile \
+ https://github.com/dxdxdt/gists/raw/master/flock_mmap/flock_mmap.c \
+ https://github.com/dxdxdt/gists/raw/master/flock_mmap/README.md
+
+chmod 755 tests.sh
+```
+
+## Run it
+```sh
+make 2> /dev/null
+```
+
+### Result A
+- Linux
+
+```
+./tests.sh
+[TEST]fcntl(fd, F_SETLK, ...) and close() unlocks the file: YES
+[TEST]fcntl(fd, F_SETLK, ...) and holding unlocks the file: NO
+[TEST]flock() and close() unlocks the file*: NO
+[TEST]flock() and holding unlocks the file : NO
+```
+
+### Result B
+- FreeBSD
+- OpenBSD
+- mac
+
+```
+./tests.sh
+[TEST]fcntl(fd, F_SETLK, ...) and close() unlocks the file: YES
+[TEST]fcntl(fd, F_SETLK, ...) and holding unlocks the file: NO
+[TEST]flock() and close() unlocks the file*: YES
+[TEST]flock() and holding unlocks the file : NO
+```
diff --git a/flock_mmap/flock_mmap.c b/flock_mmap/flock_mmap.c
new file mode 100644
index 0000000..7954a40
--- /dev/null
+++ b/flock_mmap/flock_mmap.c
@@ -0,0 +1,118 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+static const size_t M_SIZE = 1;
+static bool use_fcntl = false;
+static bool close_fd = true;
+static bool should_wait = false;
+static bool verbose = false;
+static const char *path;
+
+static bool parse_args (const int argc, const char **argv) {
+ int opt;
+
+ while ((opt = getopt(argc, (char *const*)argv, "fnwV")) != -1) {
+ switch (opt) {
+ case 'f': use_fcntl = true; break;
+ case 'n': close_fd = false; break;
+ case 'w': should_wait = true; break;
+ case 'V': verbose = true; break;
+ default: return false;
+ }
+ }
+
+ if (optind >= argc) {
+ return false;
+ }
+ else {
+ path = argv[optind];
+ }
+
+ return true;
+}
+
+int main (const int argc, const char **argv) {
+ int ec = 0;
+ int fd = -1;
+ int f_ret;
+ void *m = MAP_FAILED;
+
+ if (!parse_args(argc, argv)) {
+ ec = 2;
+ fprintf(stderr, "Usage: %s -fnw <lock file path>\n", argv[0]);
+ goto END;
+ }
+
+ if (!verbose) {
+ close(STDOUT_FILENO);
+ }
+
+ fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0755);
+ if (fd < 0) {
+ ec = 1;
+ perror(path);
+ goto END;
+ }
+
+ if (use_fcntl) {
+ struct flock fl;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_len = M_SIZE;
+
+ f_ret = fcntl(fd, F_SETLK, &fl);
+ }
+ else {
+ f_ret = flock(fd, LOCK_EX | LOCK_NB);
+ }
+
+ if (f_ret != 0) {
+ ec = 1;
+ perror(path);
+ goto END;
+ }
+
+ f_ret = ftruncate(fd, M_SIZE);
+ if (f_ret != 0) {
+ ec = 1;
+ perror(path);
+ goto END;
+ }
+
+ m = mmap(NULL, M_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == MAP_FAILED) {
+ ec = 1;
+ perror(path);
+ goto END;
+ }
+
+ // This block
+ if (close_fd) {
+ close(fd);
+ fd = -1;
+ }
+
+ printf("Lock holding\n");
+ if (should_wait) {
+ pause();
+ }
+
+END:
+ if (fd >= 0) {
+ close(fd);
+ }
+ if (m != MAP_FAILED) {
+ munmap(m, M_SIZE);
+ }
+ return ec;
+}
diff --git a/flock_mmap/tests.sh b/flock_mmap/tests.sh
new file mode 100755
index 0000000..4cad797
--- /dev/null
+++ b/flock_mmap/tests.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+LOCK_F="lock"
+
+setyes () {
+ echo "[TEST]$TEST_NAME: YES"
+}
+
+setno () {
+ echo "[TEST]$TEST_NAME: NO"
+}
+
+cleanup () {
+ kill -TERM $!
+}
+
+TEST_NAME="fcntl(fd, F_SETLK, ...) and close() unlocks the file"
+./flock_mmap -fw "$LOCK_F" & sleep 1
+if ./flock_mmap -f "$LOCK_F"
+then
+ setyes
+else
+ setno
+fi
+cleanup
+
+TEST_NAME="fcntl(fd, F_SETLK, ...) and holding unlocks the file"
+./flock_mmap -fwn "$LOCK_F" & sleep 1
+if ./flock_mmap -f "$LOCK_F"
+then
+ setyes
+else
+ setno
+fi
+cleanup
+
+TEST_NAME="flock() and close() unlocks the file*"
+./flock_mmap -w "$LOCK_F" & sleep 1
+if ./flock_mmap "$LOCK_F"
+then
+ setyes
+else
+ setno
+fi
+cleanup
+
+TEST_NAME="flock() and holding unlocks the file "
+./flock_mmap -wn "$LOCK_F" & sleep 1
+if ./flock_mmap "$LOCK_F"
+then
+ setyes
+else
+ setno
+fi
+cleanup