diff --git a/MODULES.html.sh b/MODULES.html.sh index 058125d..ccce428 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -396,6 +396,7 @@ fgets fgetwc fgetws fileno +flock flockfile floor floorf @@ -2105,6 +2106,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..142fcc4 --- /dev/null +++ b/lib/flock.c @@ -0,0 +1,232 @@ +/* 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 + +/* LockFile and LockFileEx */ +#define WIN32_LEAN_AND_MEAN +#include + +#include + +/* Is this platform NT? NT has LockFileEx which can do reader/writer + * locks, everything before that only has LockFile which can just lock + * files. This is not implemented for WinCE (which lacks both). + * + * os_type is: + * 0 = not determined yet + * 1 = 95/98/ME + * 2 = NT + */ +static int os_type = 0; + +static int +is_NT () +{ + if (os_type==0) + { + OSVERSIONINFO sInfo; + + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + GetVersionEx(&sInfo); + os_type = sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ? 2 : 1; + } + return os_type == 2; +} + +/* 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. Use GetFileSize since it's available on all + * versions of Windows (GetFileSizeEx is less braindead but only + * available on NT). + */ +static int +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 reader lock. */ +static int +do_lock_read (HANDLE h, int non_blocking) +{ + int res; + DWORD size_lower, size_upper; + + res = file_size (h, &size_lower, &size_upper); + if (!res) return 0; + + if (is_NT()) + { + OVERLAPPED ovlp; + int flags = 0; + + /* NT is relatively sane. Request a non-exclusive lock on + * the whole file. + */ + ovlp.Offset = 0; + ovlp.OffsetHigh = 0; + ovlp.hEvent = 0; + if (non_blocking) flags |= LOCKFILE_FAIL_IMMEDIATELY; + res = LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); + } + else + { + int lk; + + /* Pre-NT systems have only LockFile, which only locks + * exclusively. Use the same strategy as sqlite: acquire a lock + * on a single byte somewhere in the file. Two readers will + * usually be able to continue in parallel (unless they are + * unlucky and request the same byte -- of course the size of + * the file matters here). A writer will try to lock the whole + * file, so always get exclusive access. + */ + /* XXX rand() isn't thread-safe. */ + /* XXX sqlite original code saves 'lk' (the offset of the byte + * we are locking). We have nowhere convenient to stash this, + * but it shouldn't matter - we unlock the whole file below. + */ + lk = rand () % size_lower; + res = LockFile (h, lk, 0, 1, 0); + } + + return res; +} + +/* Get an exclusive lock on a file. */ +static int +do_lock_exclusive (HANDLE h, int non_blocking) +{ + int res; + DWORD size_lower, size_upper; + + res = file_size (h, &size_lower, &size_upper); + if (!res) return 0; + + if (is_NT()) + { + OVERLAPPED ovlp; + int flags = LOCKFILE_EXCLUSIVE_LOCK; + + ovlp.Offset = 0; + ovlp.OffsetHigh = 0; + ovlp.hEvent = 0; + if (non_blocking) flags |= LOCKFILE_FAIL_IMMEDIATELY; + res = LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); + } + else + { + /* See discussion in do_lock_read function above. */ + res = LockFile (h, 0, 0, size_lower, size_upper); + } + + return res; +} + +/* Unlock reader or exclusive lock. */ +static int +do_unlock (HANDLE h) +{ + int res; + DWORD size_lower, size_upper; + + res = file_size (h, &size_lower, &size_upper); + if (!res) return 0; + + res = UnlockFile(h, 0, 0, size_lower, size_upper); + return res; +} + +int +flock (int fd, int operation) +{ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD res; + int non_blocking = operation & LOCK_NB; + + if (h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + if (operation & LOCK_SH) + res = do_lock_read (h, non_blocking); + else if (operation & LOCK_EX) + res = do_lock_exclusive (h, non_blocking); + else if (operation & LOCK_UN) + res = do_unlock (h); + else { + 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: + /* EWOULDBLOCK is not available on MinGW. */ + errno = EAGAIN; + break; + + /* Out of memory. */ + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + + /* Unlikely to be other errors, but at least don't lose the + * error code. + */ + default: + errno = err; + } + + return -1; + } + + return 0; +} + +#else /* !Windows */ + +#error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." + +#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..54ef7cf --- /dev/null +++ b/m4/flock.m4 @@ -0,0 +1,19 @@ +# 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 +]) + +# Prerequisites of lib/flock.c. +AC_DEFUN([gl_PREREQ_FLOCK], [:]) 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/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