>From 2f5d884b626a6410662826d0748f03ba297e76b2 Mon Sep 17 00:00:00 2001 From: Richard W.M. Jones Date: Fri, 3 Oct 2008 14:58:14 +0100 Subject: [PATCH] Implement flock. * MODULES.html.sh: Add to list of modules. * doc/glibc-functions/flock.texi: Update documentation. * lib/flock.c: flock implementation for Windows and Unix systems which have fcntl. * lib/sys_file.in.h: header file. * m4/flock.m4: M4 macros. * m4/sys_file_h.m4: M4 macros for replacement sys/file.h. * modules/flock: flock module. * modules/flock-tests: flock tests module. * modules/sys_file: sys/file.h module. * tests/test-flock.c: test suite for flock. --- MODULES.html.sh | 2 + doc/glibc-functions/flock.texi | 7 +- lib/flock.c | 219 ++++++++++++++++++++++++++++++++++++++++ lib/sys_file.in.h | 58 +++++++++++ m4/flock.m4 | 26 +++++ m4/sys_file_h.m4 | 41 ++++++++ modules/flock | 24 +++++ modules/flock-tests | 10 ++ modules/sys_file | 44 ++++++++ tests/test-flock.c | 103 +++++++++++++++++++ 10 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 lib/flock.c create mode 100644 lib/sys_file.in.h create mode 100644 m4/flock.m4 create mode 100644 m4/sys_file_h.m4 create mode 100644 modules/flock create mode 100644 modules/flock-tests create mode 100644 modules/sys_file create mode 100644 tests/test-flock.c diff --git a/MODULES.html.sh b/MODULES.html.sh index 229fb53..96fe700 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -396,6 +396,7 @@ fgets fgetwc fgetws fileno +flock flockfile floor floorf @@ -2106,6 +2107,7 @@ func_all_modules () func_module errno func_module fchdir func_module fcntl + func_module flock func_module fopen func_module fprintf-posix func_module freopen diff --git a/doc/glibc-functions/flock.texi b/doc/glibc-functions/flock.texi index 29a2eb8..23878cf 100644 --- a/doc/glibc-functions/flock.texi +++ b/doc/glibc-functions/flock.texi @@ -2,15 +2,18 @@ @subsection @code{flock} @findex flock -Gnulib module: --- +Gnulib module: flock Portability problems fixed by Gnulib: @itemize address@hidden +This function is missing on some platforms: +mingw @end itemize Portability problems not fixed by Gnulib: @itemize @item This function is missing on some platforms: -AIX 5.1, HP-UX 11, Solaris 10, mingw, BeOS. +AIX 5.1, HP-UX 11, Solaris 10, BeOS. @end itemize diff --git a/lib/flock.c b/lib/flock.c new file mode 100644 index 0000000..5aa7690 --- /dev/null +++ b/lib/flock.c @@ -0,0 +1,219 @@ +/* Emulate flock on platforms that lack it, primarily Windows and MinGW. + + This is derived from sqlite3 sources. + http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/os_win.c + http://www.sqlite.org/copyright.html + + Written by Richard W.M. Jones + + Copyright (C) 2008 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + +/* _get_osfhandle */ +#include + +/* LockFileEx */ +#define WIN32_LEAN_AND_MEAN +#include + +#include + +/* Determine the current size of a file. Because the other braindead + * APIs we'll call need lower/upper 32 bit pairs, keep the file size + * like that too. + */ +static BOOL +file_size (HANDLE h, DWORD * lower, DWORD * upper) +{ + *lower = GetFileSize (h, upper); + return 1; +} + +/* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */ +#ifndef LOCKFILE_FAIL_IMMEDIATELY +# define LOCKFILE_FAIL_IMMEDIATELY 1 +#endif + +/* Acquire a lock. */ +static BOOL +do_lock (HANDLE h, int non_blocking, int exclusive) +{ + BOOL res; + DWORD size_lower, size_upper; + OVERLAPPED ovlp; + int flags = 0; + + /* We're going to lock the whole file, so get the file size. */ + res = file_size (h, &size_lower, &size_upper); + if (!res) + return 0; + + /* Start offset is 0, and also zero the remaining members of this struct. */ + memset (&ovlp, 0, sizeof ovlp); + + if (non_blocking) + flags |= LOCKFILE_FAIL_IMMEDIATELY; + if (exclusive) + flags |= LOCKFILE_EXCLUSIVE_LOCK; + + return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); +} + +/* Unlock reader or exclusive lock. */ +static BOOL +do_unlock (HANDLE h) +{ + int res; + DWORD size_lower, size_upper; + + res = file_size (h, &size_lower, &size_upper); + if (!res) + return 0; + + return UnlockFile (h, 0, 0, size_lower, size_upper); +} + +/* Now our BSD-like flock operation. */ +int +flock (int fd, int operation) +{ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD res; + int non_blocking; + + if (h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + non_blocking = operation & LOCK_NB; + operation &= ~LOCK_NB; + + switch (operation) + { + case LOCK_SH: + res = do_lock (h, non_blocking, 0); + break; + case LOCK_EX: + res = do_lock (h, non_blocking, 1); + break; + case LOCK_UN: + res = do_unlock (h); + break; + default: + errno = EINVAL; + return -1; + } + + /* Map Windows errors into Unix errnos. As usual MSDN fails to + * document the permissible error codes. + */ + if (!res) + { + DWORD err = GetLastError (); + switch (err) + { + /* This means someone else is holding a lock. */ + case ERROR_LOCK_VIOLATION: + errno = EAGAIN; + break; + + /* Out of memory. */ + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + + case ERROR_BAD_COMMAND: + errno = EINVAL; + break; + + /* Unlikely to be other errors, but at least don't lose the + * error code. + */ + default: + errno = err; + } + + return -1; + } + + return 0; +} + +#else /* !Windows */ + +#ifdef HAVE_FLOCK_L_TYPE +/* We know how to implement flock in terms of fcntl. */ + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +int +flock (int fd, int operation) +{ + int cmd, r; + struct flock fl; + + if (operation & LOCK_NB) + cmd = F_SETLK; + else + cmd = F_SETLKW; + operation &= ~LOCK_NB; + + memset (&fl, 0, sizeof fl); + fl.l_whence = SEEK_SET; + /* l_start & l_len are 0, which as a special case means "whole file". */ + + switch (operation) + { + case LOCK_SH: + fl.l_type = F_RDLCK; + break; + case LOCK_EX: + fl.l_type = F_WRLCK; + break; + case LOCK_UN: + fl.l_type = F_UNLCK; + break; + default: + errno = EINVAL; + return -1; + } + + r = fcntl (fd, cmd, &fl); + if (r == -1 && errno == EACCES) + errno = EAGAIN; + + return r; +} + +#else /* !HAVE_FLOCK_L_TYPE */ + +#error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." + +#endif /* !HAVE_FLOCK_L_TYPE */ + +#endif /* !Windows */ diff --git a/lib/sys_file.in.h b/lib/sys_file.in.h new file mode 100644 index 0000000..c1d2315 --- /dev/null +++ b/lib/sys_file.in.h @@ -0,0 +1,58 @@ +/* Provide a more complete sys/file.h. + + Copyright (C) 2007-2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Richard W.M. Jones. */ +#ifndef _GL_SYS_FILE_H + address@hidden@ + +/* The include_next requires a split double-inclusion guard. */ +# if @HAVE_SYS_FILE_H@ +# @INCLUDE_NEXT@ @NEXT_SYS_FILE_H@ +# endif + +#ifndef _GL_SYS_FILE_H +#define _GL_SYS_FILE_H + + +#if @GNULIB_FLOCK@ +/* Apply or remove advisory locks on an open file. + Return 0 if successful, otherwise -1 and errno set. */ +# if address@hidden@ +extern int flock (int fd, int operation); + +/* Operations for the 'flock' call (same as Linux kernel constants). */ +#define LOCK_SH 1 /* Shared lock. */ +#define LOCK_EX 2 /* Exclusive lock. */ +#define LOCK_UN 8 /* Unlock. */ + +/* Can be OR'd in to one of the above. */ +#define LOCK_NB 4 /* Don't block when locking. */ + +# endif +#elif defined GNULIB_POSIXCHECK +# undef flock +# define flock(fd,op) \ + (GL_LINK_WARNING ("flock is unportable - " \ + "use gnulib module flock for portability"), \ + flock ((fd), (op))) +#endif + + +#endif /* _GL_SYS_FILE_H */ +#endif /* _GL_SYS_FILE_H */ diff --git a/m4/flock.m4 b/m4/flock.m4 new file mode 100644 index 0000000..96475fc --- /dev/null +++ b/m4/flock.m4 @@ -0,0 +1,26 @@ +# flock.m4 serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_FLOCK], +[ + AC_REQUIRE([gl_HEADER_SYS_FILE_H_DEFAULTS]) + AC_CHECK_FUNCS_ONCE([flock]) + if test $ac_cv_func_flock = no; then + HAVE_FLOCK=0 + AC_LIBOBJ([flock]) + gl_PREREQ_FLOCK + fi +]) + +dnl Prerequisites of lib/flock.c. +AC_DEFUN([gl_PREREQ_FLOCK], +[ + AC_CHECK_FUNCS_ONCE([fcntl]) + AC_CHECK_HEADERS_ONCE([unistd.h fcntl.h]) + + dnl Do we have a POSIX fcntl lock implementation? + AC_CHECK_MEMBERS([struct flock.l_type],[],[],[[#include ]]) +]) diff --git a/m4/sys_file_h.m4 b/m4/sys_file_h.m4 new file mode 100644 index 0000000..436c6fe --- /dev/null +++ b/m4/sys_file_h.m4 @@ -0,0 +1,41 @@ +# Configure a replacement for . + +# Copyright (C) 2008 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Written by Richard W.M. Jones. + +AC_DEFUN([gl_HEADER_SYS_FILE_H], +[ + AC_REQUIRE([gl_HEADER_SYS_FILE_H_DEFAULTS]) + + dnl Only flock is defined in a working . If that + dnl function is already there, we don't want to do any substitution. + AC_CHECK_FUNCS_ONCE([flock]) + + gl_CHECK_NEXT_HEADERS([sys/file.h]) + SYS_FILE_H='sys/file.h' + AC_SUBST([SYS_FILE_H]) + + AC_CHECK_HEADERS_ONCE([sys/file.h]) + if test $ac_cv_header_sys_file_h = yes; then + HAVE_SYS_FILE_H=1 + else + HAVE_SYS_FILE_H=0 + fi + AC_SUBST([HAVE_SYS_FILE_H]) +]) + +AC_DEFUN([gl_HEADER_SYS_FILE_MODULE_INDICATOR], +[ + AC_REQUIRE([gl_HEADER_SYS_FILE_H_DEFAULTS]) + GNULIB_[]m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])=1 +]) + +AC_DEFUN([gl_HEADER_SYS_FILE_H_DEFAULTS], +[ + GNULIB_FLOCK=0; AC_SUBST([GNULIB_FLOCK]) + HAVE_FLOCK=1; AC_SUBST([HAVE_FLOCK]) +]) diff --git a/modules/flock b/modules/flock new file mode 100644 index 0000000..9134d51 --- /dev/null +++ b/modules/flock @@ -0,0 +1,24 @@ +Description: +flock(2) function: advisory file locks. + +Files: +lib/flock.c +m4/flock.m4 + +Depends-on: +sys_file + +configure.ac: +gl_FUNC_FLOCK +gl_HEADER_SYS_FILE_MODULE_INDICATOR([flock]) + +Makefile.am: + +Include: + + +License: +LGPLv2+ + +Maintainer: +Richard W.M. Jones, Jim Meyering diff --git a/modules/flock-tests b/modules/flock-tests new file mode 100644 index 0000000..d43d10c --- /dev/null +++ b/modules/flock-tests @@ -0,0 +1,10 @@ +Files: +tests/test-flock.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-flock +check_PROGRAMS += test-flock diff --git a/modules/sys_file b/modules/sys_file new file mode 100644 index 0000000..2c1f8f8 --- /dev/null +++ b/modules/sys_file @@ -0,0 +1,44 @@ +Description: + for systems with an incomplete header. + +Files: +lib/sys_file.in.h +m4/sys_file_h.m4 + +Depends-on: +include_next +link-warning + +configure.ac: +gl_HEADER_SYS_FILE_H +AC_PROG_MKDIR_P + +Makefile.am: +BUILT_SOURCES += $(SYS_FILE_H) + +# We need the following in order to create when the system +# has one that is incomplete. +sys/file.h: sys_file.in.h + @MKDIR_P@ sys + rm -f address@hidden $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's/@''HAVE_SYS_FILE_H''@/$(HAVE_SYS_FILE_H)/g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''NEXT_SYS_FILE_H''@|$(NEXT_SYS_FILE_H)|g' \ + -e 's/@''HAVE_FLOCK''@/$(HAVE_FLOCK)/g' \ + -e 's/@''GNULIB_FLOCK''@/$(GNULIB_FLOCK)/g' \ + < $(srcdir)/sys_file.in.h; \ + } > address@hidden + mv address@hidden $@ +MOSTLYCLEANFILES += sys/file.h sys/file.h-t +MOSTLYCLEANDIRS += sys + +Include: +#include + +License: +LGPLv2+ + +Maintainer: +all diff --git a/tests/test-flock.c b/tests/test-flock.c new file mode 100644 index 0000000..e7dac2b --- /dev/null +++ b/tests/test-flock.c @@ -0,0 +1,103 @@ +/* Test of flock() function. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include +#include + +#include + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed, errno = %d\n", \ + __FILE__, __LINE__, errno); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +static void +test_shared (const char *file, int fd) +{ + /* Should be able to acquire several shared locks on a file, through + * different file table entries. + */ + int fd2, r; + + ASSERT (flock (fd, LOCK_SH) == 0); + + fd2 = open (file, O_RDWR, 0644); + ASSERT (fd2 >= 0); + + r = flock (fd2, LOCK_SH | LOCK_NB); + ASSERT (r == 0); /* Was able to acquire a second shared lock. */ + + ASSERT (flock (fd, LOCK_UN) == 0); + ASSERT (close (fd2) == 0); +} + +static void +test_exclusive (const char *file, int fd) +{ + /* Should not be able to acquire more than one exclusive lock on a file. */ + int fd2, r; + + ASSERT (flock (fd, LOCK_EX) == 0); + + fd2 = open (file, O_RDWR, 0644); + ASSERT (fd2 >= 0); + + r = flock (fd2, LOCK_SH | LOCK_NB); + ASSERT (r == -1); /* Was unable to acquire a second exclusive lock. */ + + ASSERT (flock (fd, LOCK_UN) == 0); + ASSERT (close (fd2) == 0); +} + +int +main (int argc, char *argv[]) +{ + int fd; + const char *file = "test-flock.txt"; + + /* Open a non-empty file for testing. */ + fd = open (file, O_RDWR | O_CREAT | O_TRUNC, 0644); + ASSERT (fd >= 0); + ASSERT (write (fd, "hello", 5) == 5); + + /* Some impossible operation codes which should never be accepted. */ + ASSERT (flock (fd, LOCK_SH | LOCK_EX) == -1); + ASSERT (errno == EINVAL); + ASSERT (flock (fd, LOCK_SH | LOCK_UN) == -1); + ASSERT (errno == EINVAL); + ASSERT (flock (fd, LOCK_EX | LOCK_UN) == -1); + ASSERT (errno == EINVAL); + ASSERT (flock (fd, 0) == -1); + ASSERT (errno == EINVAL); + + test_shared (file, fd); + test_exclusive (file, fd); + + /* Close and remove the test file. */ + ASSERT (close (fd) == 0); + ASSERT (unlink (file) == 0); + + return 0; +} -- 1.6.0.1