(root)/
man-db-2.12.0/
gl/
lib/
flock.c
       1  /* Emulate flock on platforms that lack it, primarily Windows and MinGW.
       2  
       3     This is derived from sqlite3 sources.
       4     https://www.sqlite.org/src/finfo?name=src/os_win.c
       5     https://www.sqlite.org/copyright.html
       6  
       7     Written by Richard W.M. Jones <rjones.at.redhat.com>
       8  
       9     Copyright (C) 2008-2023 Free Software Foundation, Inc.
      10  
      11     This library is free software; you can redistribute it and/or
      12     modify it under the terms of the GNU Lesser General Public
      13     License as published by the Free Software Foundation; either
      14     version 2.1 of the License, or (at your option) any later version.
      15  
      16     This library is distributed in the hope that it will be useful,
      17     but WITHOUT ANY WARRANTY; without even the implied warranty of
      18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      19     Lesser General Public License for more details.
      20  
      21     You should have received a copy of the GNU Lesser General Public License
      22     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      23  
      24  #include <config.h>
      25  #include <sys/file.h>
      26  
      27  #if defined _WIN32 && ! defined __CYGWIN__
      28  
      29  /* LockFileEx */
      30  # define WIN32_LEAN_AND_MEAN
      31  # include <windows.h>
      32  
      33  # include <errno.h>
      34  
      35  /* _get_osfhandle */
      36  # if GNULIB_MSVC_NOTHROW
      37  #  include "msvc-nothrow.h"
      38  # else
      39  #  include <io.h>
      40  # endif
      41  
      42  /* Determine the current size of a file.  Because the other braindead
      43   * APIs we'll call need lower/upper 32 bit pairs, keep the file size
      44   * like that too.
      45   */
      46  static BOOL
      47  file_size (HANDLE h, DWORD * lower, DWORD * upper)
      48  {
      49    *lower = GetFileSize (h, upper);
      50    return 1;
      51  }
      52  
      53  /* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */
      54  # ifndef LOCKFILE_FAIL_IMMEDIATELY
      55  #  define LOCKFILE_FAIL_IMMEDIATELY 1
      56  # endif
      57  
      58  /* Acquire a lock. */
      59  static BOOL
      60  do_lock (HANDLE h, int non_blocking, int exclusive)
      61  {
      62    BOOL res;
      63    DWORD size_lower, size_upper;
      64    OVERLAPPED ovlp;
      65    int flags = 0;
      66  
      67    /* We're going to lock the whole file, so get the file size. */
      68    res = file_size (h, &size_lower, &size_upper);
      69    if (!res)
      70      return 0;
      71  
      72    /* Start offset is 0, and also zero the remaining members of this struct. */
      73    memset (&ovlp, 0, sizeof ovlp);
      74  
      75    if (non_blocking)
      76      flags |= LOCKFILE_FAIL_IMMEDIATELY;
      77    if (exclusive)
      78      flags |= LOCKFILE_EXCLUSIVE_LOCK;
      79  
      80    return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp);
      81  }
      82  
      83  /* Unlock reader or exclusive lock. */
      84  static BOOL
      85  do_unlock (HANDLE h)
      86  {
      87    int res;
      88    DWORD size_lower, size_upper;
      89  
      90    res = file_size (h, &size_lower, &size_upper);
      91    if (!res)
      92      return 0;
      93  
      94    return UnlockFile (h, 0, 0, size_lower, size_upper);
      95  }
      96  
      97  /* Now our BSD-like flock operation. */
      98  int
      99  flock (int fd, int operation)
     100  {
     101    HANDLE h = (HANDLE) _get_osfhandle (fd);
     102    DWORD res;
     103    int non_blocking;
     104  
     105    if (h == INVALID_HANDLE_VALUE)
     106      {
     107        errno = EBADF;
     108        return -1;
     109      }
     110  
     111    non_blocking = operation & LOCK_NB;
     112    operation &= ~LOCK_NB;
     113  
     114    switch (operation)
     115      {
     116      case LOCK_SH:
     117        res = do_lock (h, non_blocking, 0);
     118        break;
     119      case LOCK_EX:
     120        res = do_lock (h, non_blocking, 1);
     121        break;
     122      case LOCK_UN:
     123        res = do_unlock (h);
     124        break;
     125      default:
     126        errno = EINVAL;
     127        return -1;
     128      }
     129  
     130    /* Map Windows errors into Unix errnos.  As usual MSDN fails to
     131     * document the permissible error codes.
     132     */
     133    if (!res)
     134      {
     135        DWORD err = GetLastError ();
     136        switch (err)
     137          {
     138            /* This means someone else is holding a lock. */
     139          case ERROR_LOCK_VIOLATION:
     140            errno = EAGAIN;
     141            break;
     142  
     143            /* Out of memory. */
     144          case ERROR_NOT_ENOUGH_MEMORY:
     145            errno = ENOMEM;
     146            break;
     147  
     148          case ERROR_BAD_COMMAND:
     149            errno = EINVAL;
     150            break;
     151  
     152            /* Unlikely to be other errors, but at least don't lose the
     153             * error code.
     154             */
     155          default:
     156            errno = err;
     157          }
     158  
     159        return -1;
     160      }
     161  
     162    return 0;
     163  }
     164  
     165  #else /* !Windows */
     166  
     167  # ifdef HAVE_STRUCT_FLOCK_L_TYPE
     168  /* We know how to implement flock in terms of fcntl. */
     169  
     170  #  include <fcntl.h>
     171  
     172  #  ifdef HAVE_UNISTD_H
     173  #   include <unistd.h>
     174  #  endif
     175  
     176  #  include <errno.h>
     177  #  include <string.h>
     178  
     179  int
     180  flock (int fd, int operation)
     181  {
     182    int cmd, r;
     183    struct flock fl;
     184  
     185    if (operation & LOCK_NB)
     186      cmd = F_SETLK;
     187    else
     188      cmd = F_SETLKW;
     189    operation &= ~LOCK_NB;
     190  
     191    memset (&fl, 0, sizeof fl);
     192    fl.l_whence = SEEK_SET;
     193    /* l_start & l_len are 0, which as a special case means "whole file". */
     194  
     195    switch (operation)
     196      {
     197      case LOCK_SH:
     198        fl.l_type = F_RDLCK;
     199        break;
     200      case LOCK_EX:
     201        fl.l_type = F_WRLCK;
     202        break;
     203      case LOCK_UN:
     204        fl.l_type = F_UNLCK;
     205        break;
     206      default:
     207        errno = EINVAL;
     208        return -1;
     209      }
     210  
     211    r = fcntl (fd, cmd, &fl);
     212    if (r == -1 && errno == EACCES)
     213      errno = EAGAIN;
     214  
     215    return r;
     216  }
     217  
     218  # else /* !HAVE_STRUCT_FLOCK_L_TYPE */
     219  
     220  #  error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
     221  
     222  # endif /* !HAVE_STRUCT_FLOCK_L_TYPE */
     223  
     224  #endif /* !Windows */