From 5fc02e2e847608a67d46af04d5f935cd889b23c3 Mon Sep 17 00:00:00 2001 From: David Timber Date: Fri, 22 Mar 2024 10:58:42 +0900 Subject: Add flock_mmap --- flock_mmap/.gitignore | 2 + flock_mmap/Makefile | 15 ++++++ flock_mmap/README.md | 50 ++++++++++++++++++++ flock_mmap/flock_mmap.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ flock_mmap/tests.sh | 54 ++++++++++++++++++++++ 5 files changed, 239 insertions(+) create mode 100644 flock_mmap/.gitignore create mode 100644 flock_mmap/Makefile create mode 100644 flock_mmap/README.md create mode 100644 flock_mmap/flock_mmap.c create mode 100755 flock_mmap/tests.sh 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +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 \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 -- cgit