flock.c 4.9 KB

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