]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/flock.cc
* cygerrno.h (geterrno_from_nt_status): Declare.
[newlib-cygwin.git] / winsup / cygwin / flock.cc
CommitLineData
a998dd70 1/* flock.cc. NT specific implementation of advisory file locking.
f3a1e23e 2
86bf572e 3 Copyright 2003, 2008, 2009, 2010, 2011 Red Hat, Inc.
f3a1e23e
CV
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
a998dd70
CV
11/* The basic mechanism as well as the datastructures used in the below
12 implementation are taken from the FreeBSD repository on 2008-03-18.
13 The essential code of the lf_XXX functions has been taken from the
14 module src/sys/kern/kern_lockf.c. It has been adapted to use NT
15 global namespace subdirs and event objects for synchronization
025c1fac
CF
16 purposes.
17
a998dd70
CV
18 So, the following copyright applies to most of the code in the lf_XXX
19 functions.
20
21 * Copyright (c) 1982, 1986, 1989, 1993
22 * The Regents of the University of California. All rights reserved.
23 *
24 * This code is derived from software contributed to Berkeley by
25 * Scooter Morris at Genentech Inc.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 4. Neither the name of the University nor the names of its contributors
36 * may be used to endorse or promote products derived from this software
37 * without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
50 *
51 * @(#)ufs_lockf.c 8.3 (Berkeley) 1/6/94
025c1fac 52*/
a998dd70
CV
53
54/*
55 * The flock() function is based upon source taken from the Red Hat
56 * implementation used in their imap-2002d SRPM.
57 *
58 * $RH: flock.c,v 1.2 2000/08/23 17:07:00 nalin Exp $
59 */
60
fabfb1a1 61/* The lockf function is based upon FreeBSD sources with the following
a998dd70 62 * copyright.
8c0b8bc3
CV
63 */
64/*
a998dd70
CV
65 * Copyright (c) 1997 The NetBSD Foundation, Inc.
66 * All rights reserved.
67 *
68 * This code is derived from software contributed to The NetBSD Foundation
69 * by Klaus Klein.
70 *
71 * Redistribution and use in source and binary forms, with or without
72 * modification, are permitted provided that the following conditions
73 * are met:
74 * 1. Redistributions of source code must retain the above copyright
75 * notice, this list of conditions and the following disclaimer.
76 * 2. Redistributions in binary form must reproduce the above copyright
77 * notice, this list of conditions and the following disclaimer in the
78 * documentation and/or other materials provided with the distribution.
a998dd70
CV
79 *
80 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
81 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
82 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
83 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
84 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
85 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
86 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
87 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
88 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
89 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
90 * POSSIBILITY OF SUCH DAMAGE.
8c0b8bc3 91 */
a998dd70 92
e2a39e2e 93#include "winsup.h"
a998dd70 94#include <assert.h>
f3a1e23e 95#include <sys/file.h>
f3a1e23e 96#include <unistd.h>
a998dd70
CV
97#include <stdlib.h>
98#include "cygerrno.h"
a998dd70 99#include "security.h"
db5ae618 100#include "shared_info.h"
a998dd70
CV
101#include "path.h"
102#include "fhandler.h"
103#include "dtable.h"
104#include "cygheap.h"
a998dd70
CV
105#include "pinfo.h"
106#include "sigproc.h"
107#include "cygtls.h"
30bbc55e 108#include "tls_pbuf.h"
a998dd70
CV
109#include "ntdll.h"
110#include <sys/queue.h>
111#include <wchar.h>
112
a998dd70
CV
113#define F_WAIT 0x10 /* Wait until lock is granted */
114#define F_FLOCK 0x20 /* Use flock(2) semantics for lock */
115#define F_POSIX 0x40 /* Use POSIX semantics for lock */
116
117#ifndef OFF_MAX
118#define OFF_MAX LLONG_MAX
119#endif
120
121static NO_COPY muto lockf_guard;
122
123#define INODE_LIST_LOCK() (lockf_guard.init ("lockf_guard")->acquire ())
124#define INODE_LIST_UNLOCK() (lockf_guard.release ())
125
636c94d8 126#define LOCK_OBJ_NAME_LEN 64
a998dd70 127
db5ae618
CV
128#define FLOCK_INODE_DIR_ACCESS (DIRECTORY_QUERY \
129 | DIRECTORY_TRAVERSE \
130 | DIRECTORY_CREATE_OBJECT \
131 | READ_CONTROL)
132
db5ae618
CV
133#define FLOCK_EVENT_ACCESS (EVENT_QUERY_STATE \
134 | SYNCHRONIZE \
135 | READ_CONTROL)
136
a998dd70 137/* This function takes the own process security descriptor DACL and adds
b9e2579c
CV
138 SYNCHRONIZE permissions for everyone. This allows all processes
139 to wait for this process to die when blocking in a F_SETLKW on a lock
140 which is hold by this process. */
a998dd70
CV
141static void
142allow_others_to_sync ()
143{
144 static NO_COPY bool done;
145
146 if (done)
147 return;
148
149 NTSTATUS status;
150 PACL dacl;
151 LPVOID ace;
152 ULONG len;
153
30bbc55e
CV
154 /* Get this process DACL. We use a rather small stack buffer here which
155 should be more than sufficient for process ACLs. Can't use tls functions
156 at this point because this gets called during initialization when the tls
157 is not really available. */
4e8f539f
CV
158#define MAX_PROCESS_SD_SIZE 3072
159 PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) alloca (MAX_PROCESS_SD_SIZE);
f16706de
CV
160 status = NtQuerySecurityObject (NtCurrentProcess (),
161 DACL_SECURITY_INFORMATION, sd,
4e8f539f 162 MAX_PROCESS_SD_SIZE, &len);
a998dd70
CV
163 if (!NT_SUCCESS (status))
164 {
165 debug_printf ("NtQuerySecurityObject: %p", status);
166 return;
167 }
30bbc55e 168 /* Create a valid dacl pointer and set its size to be as big as
a998dd70
CV
169 there's room in the temporary buffer. Note that the descriptor
170 is in self-relative format. */
171 dacl = (PACL) ((char *) sd + (uintptr_t) sd->Dacl);
172 dacl->AclSize = NT_MAX_PATH * sizeof (WCHAR) - ((char *) dacl - (char *) sd);
173 /* Allow everyone to SYNCHRONIZE with this process. */
174 if (!AddAccessAllowedAce (dacl, ACL_REVISION, SYNCHRONIZE,
175 well_known_world_sid))
176 {
177 debug_printf ("AddAccessAllowedAce: %lu", GetLastError ());
178 return;
179 }
180 /* Set the size of the DACL correctly. */
1754539e
CV
181 status = RtlFirstFreeAce (dacl, &ace);
182 if (!NT_SUCCESS (status))
a998dd70 183 {
1754539e 184 debug_printf ("RtlFirstFreeAce: %p", status);
a998dd70
CV
185 return;
186 }
187 dacl->AclSize = (char *) ace - (char *) dacl;
188 /* Write the DACL back. */
f16706de 189 status = NtSetSecurityObject (NtCurrentProcess (), DACL_SECURITY_INFORMATION, sd);
a998dd70
CV
190 if (!NT_SUCCESS (status))
191 {
192 debug_printf ("NtSetSecurityObject: %p", status);
193 return;
194 }
195 done = true;
196}
197
636c94d8
CV
198/* Get the handle count of an object. */
199static ULONG
200get_obj_handle_count (HANDLE h)
201{
202 OBJECT_BASIC_INFORMATION obi;
203 NTSTATUS status;
204 ULONG hdl_cnt = 0;
205
206 status = NtQueryObject (h, ObjectBasicInformation, &obi, sizeof obi, NULL);
207 if (!NT_SUCCESS (status))
208 debug_printf ("NtQueryObject: %p\n", status);
209 else
210 hdl_cnt = obi.HandleCount;
211 return hdl_cnt;
212}
213
a998dd70
CV
214/* Per lock class. */
215class lockf_t
216{
217 public:
218 short lf_flags; /* Semantics: F_POSIX, F_FLOCK, F_WAIT */
219 short lf_type; /* Lock type: F_RDLCK, F_WRLCK */
220 _off64_t lf_start; /* Byte # of the start of the lock */
221 _off64_t lf_end; /* Byte # of the end of the lock (-1=EOF) */
636c94d8
CV
222 long long lf_id; /* Cygwin PID for POSIX locks, a unique id per
223 file table entry for BSD flock locks. */
a998dd70
CV
224 DWORD lf_wid; /* Win PID of the resource holding the lock */
225 class lockf_t **lf_head; /* Back pointer to the head of the lockf_t list */
226 class inode_t *lf_inode; /* Back pointer to the inode_t */
227 class lockf_t *lf_next; /* Pointer to the next lock on this inode_t */
228 HANDLE lf_obj; /* Handle to the lock event object. */
229
230 lockf_t ()
231 : lf_flags (0), lf_type (0), lf_start (0), lf_end (0), lf_id (0),
636c94d8
CV
232 lf_wid (0), lf_head (NULL), lf_inode (NULL),
233 lf_next (NULL), lf_obj (NULL)
a998dd70
CV
234 {}
235 lockf_t (class inode_t *node, class lockf_t **head, short flags, short type,
636c94d8 236 _off64_t start, _off64_t end, long long id, DWORD wid)
a998dd70
CV
237 : lf_flags (flags), lf_type (type), lf_start (start), lf_end (end),
238 lf_id (id), lf_wid (wid), lf_head (head), lf_inode (node),
239 lf_next (NULL), lf_obj (NULL)
240 {}
241 ~lockf_t ();
242
31390e4c
CV
243 /* Used to create all locks list in a given TLS buffer. */
244 void *operator new (size_t size, void *p)
245 { return p; }
246 /* Used to store own lock list in the cygheap. */
a998dd70 247 void *operator new (size_t size)
31390e4c
CV
248 { return cmalloc (HEAP_FHANDLER, sizeof (lockf_t)); }
249 /* Never call on node->i_all_lf! */
a998dd70
CV
250 void operator delete (void *p)
251 { cfree (p); }
252
253 void create_lock_obj ();
636c94d8
CV
254 bool open_lock_obj ();
255 void del_lock_obj (HANDLE fhdl, bool signal = false);
a998dd70
CV
256};
257
258/* Per inode_t class */
259class inode_t
260{
261 friend class lockf_t;
262
263 public:
264 LIST_ENTRY (inode_t) i_next;
265 lockf_t *i_lockf; /* List of locks of this process. */
266 lockf_t *i_all_lf; /* Temp list of all locks for this file. */
267
636c94d8
CV
268 __dev32_t i_dev; /* Device ID */
269 __ino64_t i_ino; /* inode number */
a998dd70 270
636c94d8
CV
271 private:
272 HANDLE i_dir;
273 HANDLE i_mtx;
a74869c0
CV
274 unsigned long i_wait; /* Number of blocked threads waiting for
275 a blocking lock. */
a998dd70
CV
276
277 public:
636c94d8 278 inode_t (__dev32_t dev, __ino64_t ino);
a998dd70
CV
279 ~inode_t ();
280
281 void *operator new (size_t size)
31390e4c 282 { return cmalloc (HEAP_FHANDLER, sizeof (inode_t)); }
a998dd70
CV
283 void operator delete (void *p)
284 { cfree (p); }
285
636c94d8 286 static inode_t *get (__dev32_t dev, __ino64_t ino, bool create_if_missing);
a998dd70
CV
287
288 void LOCK () { WaitForSingleObject (i_mtx, INFINITE); }
289 void UNLOCK () { ReleaseMutex (i_mtx); }
290
a74869c0
CV
291 void wait () { ++i_wait; }
292 void unwait () { if (i_wait > 0) --i_wait; }
b4fa8164 293 bool waiting () { return i_wait > 0; }
a74869c0 294
31390e4c 295 lockf_t *get_all_locks_list ();
a998dd70 296
636c94d8 297 bool del_my_locks (long long id, HANDLE fhdl);
a998dd70
CV
298};
299
636c94d8 300inode_t::~inode_t ()
a998dd70 301{
636c94d8
CV
302 lockf_t *lock, *n_lock;
303 for (lock = i_lockf; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
304 delete lock;
305 NtClose (i_mtx);
306 NtClose (i_dir);
a998dd70
CV
307}
308
636c94d8
CV
309bool
310inode_t::del_my_locks (long long id, HANDLE fhdl)
a998dd70 311{
636c94d8
CV
312 lockf_t *lock, *n_lock;
313 lockf_t **prev = &i_lockf;
314 int lc = 0;
315 for (lock = *prev; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
316 {
317 if (lock->lf_flags & F_POSIX)
318 {
319 /* Delete all POSIX locks. */
320 *prev = n_lock;
321 ++lc;
322 delete lock;
323 }
324 else if (id && lock->lf_id == id)
325 {
326 int cnt = 0;
327 cygheap_fdenum cfd (true);
328 while (cfd.next () >= 0)
329 if (cfd->get_unique_id () == lock->lf_id && ++cnt > 1)
330 break;
331 /* Delete BSD flock lock when no other fd in this process references
332 it anymore. */
333 if (cnt <= 1)
334 {
335 *prev = n_lock;
336 lock->del_lock_obj (fhdl);
337 delete lock;
338 }
339 }
340 else
341 prev = &lock->lf_next;
342 }
343 return i_lockf == NULL;
344}
a998dd70 345
636c94d8
CV
346/* Used to delete the locks on a file hold by this process. Called from
347 close(2) and fixup_after_fork, as well as from fixup_after_exec in
348 case the close_on_exec flag is set. The whole inode is deleted as
349 soon as no lock exists on it anymore. */
350void
4a77aea0 351fhandler_base::del_my_locks (del_lock_called_from from)
636c94d8
CV
352{
353 INODE_LIST_LOCK ();
354 inode_t *node = inode_t::get (get_dev (), get_ino (), false);
355 if (node)
356 {
4a77aea0
CV
357 /* When we're called from fixup_after_exec, the fhandler is a
358 close-on-exec fhandler. In this case our io handle is already
359 invalid. We can't use it to test for the object reference count.
360 However, that shouldn't be necessary for the following reason.
361 After exec, there are no threads in the current process waiting for
362 the lock. So, either we're the only process accessing the file table
363 entry and there are no threads which require signalling, or we have
364 a parent process still accessing the file object and signalling the
365 lock event would be premature. */
636c94d8 366 bool no_locks_left =
4a77aea0
CV
367 node->del_my_locks (from == after_fork ? 0 : get_unique_id (),
368 from == after_exec ? NULL : get_handle ());
636c94d8
CV
369 if (no_locks_left)
370 {
371 LIST_REMOVE (node, i_next);
372 node->UNLOCK ();
373 delete node;
374 }
375 else
376 node->UNLOCK ();
377 }
378 INODE_LIST_UNLOCK ();
a998dd70
CV
379}
380
381/* Called in an execed child. The exec'ed process must allow SYNCHRONIZE
382 access to everyone if at least one inode exists.
636c94d8
CV
383 The lock owner's Windows PID changed and all POSIX lock event objects
384 have to be relabeled so that waiting processes know which process to
385 wait on. If the node has been abandoned due to close_on_exec on the
386 referencing fhandlers, remove the inode entirely. */
a998dd70
CV
387void
388fixup_lockf_after_exec ()
389{
636c94d8 390 inode_t *node, *next_node;
a998dd70
CV
391
392 INODE_LIST_LOCK ();
393 if (LIST_FIRST (&cygheap->inode_list))
394 allow_others_to_sync ();
636c94d8 395 LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
a998dd70 396 {
636c94d8
CV
397 int cnt = 0;
398 cygheap_fdenum cfd (true);
399 while (cfd.next () >= 0)
400 if (cfd->get_dev () == node->i_dev
401 && cfd->get_ino () == node->i_ino
402 && ++cnt > 1)
403 break;
404 if (cnt == 0)
a998dd70 405 {
636c94d8
CV
406 LIST_REMOVE (node, i_next);
407 delete node;
408 }
409 else
410 {
411 node->LOCK ();
412 for (lockf_t *lock = node->i_lockf; lock; lock = lock->lf_next)
413 if (lock->lf_flags & F_POSIX)
414 {
415 lock->del_lock_obj (NULL);
416 lock->lf_wid = myself->dwProcessId;
417 lock->create_lock_obj ();
418 }
419 node->UNLOCK ();
a998dd70 420 }
a998dd70 421 }
a998dd70
CV
422 INODE_LIST_UNLOCK ();
423}
424
425/* static method to return a pointer to the inode_t structure for a specific
426 file. The file is specified by the device and inode_t number. If inode_t
427 doesn't exist, create it. */
428inode_t *
636c94d8 429inode_t::get (__dev32_t dev, __ino64_t ino, bool create_if_missing)
a998dd70
CV
430{
431 inode_t *node;
432
433 INODE_LIST_LOCK ();
434 LIST_FOREACH (node, &cygheap->inode_list, i_next)
435 if (node->i_dev == dev && node->i_ino == ino)
436 break;
636c94d8 437 if (!node && create_if_missing)
a998dd70
CV
438 {
439 node = new inode_t (dev, ino);
31390e4c
CV
440 if (node)
441 LIST_INSERT_HEAD (&cygheap->inode_list, node, i_next);
a998dd70 442 }
636c94d8
CV
443 if (node)
444 node->LOCK ();
a998dd70
CV
445 INODE_LIST_UNLOCK ();
446 return node;
447}
448
636c94d8 449inode_t::inode_t (__dev32_t dev, __ino64_t ino)
a74869c0 450: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_wait (0L)
a998dd70
CV
451{
452 HANDLE parent_dir;
db5ae618 453 WCHAR name[48];
a998dd70
CV
454 UNICODE_STRING uname;
455 OBJECT_ATTRIBUTES attr;
456 NTSTATUS status;
457
db5ae618 458 parent_dir = get_shared_parent_dir ();
a998dd70
CV
459 /* Create a subdir which is named after the device and inode_t numbers
460 of the given file, in hex notation. */
db5ae618 461 int len = __small_swprintf (name, L"flock-%08x-%016X", dev, ino);
a998dd70 462 RtlInitCountedUnicodeString (&uname, name, len * sizeof (WCHAR));
5cb524ee
CV
463 InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT | OBJ_OPENIF,
464 parent_dir, everyone_sd (FLOCK_INODE_DIR_ACCESS));
465 status = NtCreateDirectoryObject (&i_dir, FLOCK_INODE_DIR_ACCESS, &attr);
a998dd70 466 if (!NT_SUCCESS (status))
5cb524ee 467 api_fatal ("NtCreateDirectoryObject(inode): %p", status);
a998dd70
CV
468 /* Create a mutex object in the file specific dir, which is used for
469 access synchronization on the dir and its objects. */
8deb4118 470 InitializeObjectAttributes (&attr, &ro_u_mtx, OBJ_INHERIT | OBJ_OPENIF, i_dir,
abbde487
CV
471 everyone_sd (CYG_MUTANT_ACCESS));
472 status = NtCreateMutant (&i_mtx, CYG_MUTANT_ACCESS, &attr, FALSE);
a998dd70 473 if (!NT_SUCCESS (status))
5cb524ee 474 api_fatal ("NtCreateMutant(inode): %p", status);
a998dd70
CV
475}
476
a998dd70
CV
477/* Enumerate all lock event objects for this file and create a lockf_t
478 list in the i_all_lf member. This list is searched in lf_getblock
479 for locks which potentially block our lock request. */
31390e4c
CV
480
481/* Number of lockf_t structs which fit in the temporary buffer. */
482#define MAX_LOCKF_CNT ((intptr_t)((NT_MAX_PATH * sizeof (WCHAR)) \
483 / sizeof (lockf_t)))
484
485lockf_t *
a998dd70
CV
486inode_t::get_all_locks_list ()
487{
488 struct fdbi
489 {
490 DIRECTORY_BASIC_INFORMATION dbi;
491 WCHAR buf[2][NAME_MAX + 1];
492 } f;
493 ULONG context;
494 NTSTATUS status;
31390e4c 495 lockf_t *lock = i_all_lf;
a998dd70 496
a998dd70
CV
497 for (BOOLEAN restart = TRUE;
498 NT_SUCCESS (status = NtQueryDirectoryObject (i_dir, &f, sizeof f, TRUE,
499 restart, &context, NULL));
500 restart = FALSE)
501 {
502 if (f.dbi.ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR))
503 continue;
504 wchar_t *wc = f.dbi.ObjectName.Buffer, *endptr;
636c94d8 505 /* "%02x-%01x-%016X-%016X-%016X-%08x",
a998dd70
CV
506 lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid */
507 wc[LOCK_OBJ_NAME_LEN] = L'\0';
508 short flags = wcstol (wc, &endptr, 16);
072030c3 509 if ((flags & ~(F_FLOCK | F_POSIX)) != 0
7b9e380f 510 || ((flags & (F_FLOCK | F_POSIX)) == (F_FLOCK | F_POSIX)))
a998dd70
CV
511 continue;
512 short type = wcstol (endptr + 1, &endptr, 16);
e7afe579 513 if ((type != F_RDLCK && type != F_WRLCK) || !endptr || *endptr != L'-')
025c1fac 514 continue;
a998dd70
CV
515 _off64_t start = (_off64_t) wcstoull (endptr + 1, &endptr, 16);
516 if (start < 0 || !endptr || *endptr != L'-')
025c1fac 517 continue;
a998dd70
CV
518 _off64_t end = (_off64_t) wcstoull (endptr + 1, &endptr, 16);
519 if (end < -1LL || (end > 0 && end < start) || !endptr || *endptr != L'-')
025c1fac 520 continue;
636c94d8
CV
521 long long id = wcstoll (endptr + 1, &endptr, 16);
522 if (!endptr || *endptr != L'-'
523 || ((flags & F_POSIX) && (id < 1 || id > ULONG_MAX)))
025c1fac 524 continue;
a998dd70
CV
525 DWORD wid = wcstoul (endptr + 1, &endptr, 16);
526 if (endptr && *endptr != L'\0')
025c1fac 527 continue;
31390e4c 528 if (lock - i_all_lf >= MAX_LOCKF_CNT)
025c1fac 529 {
31390e4c
CV
530 system_printf ("Warning, can't handle more than %d locks per file.",
531 MAX_LOCKF_CNT);
532 break;
533 }
534 if (lock > i_all_lf)
025c1fac 535 lock[-1].lf_next = lock;
31390e4c 536 new (lock++) lockf_t (this, &i_all_lf, flags, type, start, end, id, wid);
a998dd70 537 }
31390e4c
CV
538 /* If no lock has been found, return NULL. */
539 if (lock == i_all_lf)
540 return NULL;
541 return i_all_lf;
a998dd70
CV
542}
543
544/* Create the lock event object in the file's subdir in the NT global
545 namespace. The name is constructed from the lock properties which
546 identify it uniquely, all values in hex. See the __small_swprintf
547 call right at the start. */
548void
549lockf_t::create_lock_obj ()
550{
636c94d8 551 WCHAR name[LOCK_OBJ_NAME_LEN + 1];
a998dd70
CV
552 UNICODE_STRING uname;
553 OBJECT_ATTRIBUTES attr;
554 NTSTATUS status;
555
636c94d8 556 __small_swprintf (name, L"%02x-%01x-%016X-%016X-%016X-%08x",
072030c3
CV
557 lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start,
558 lf_end, lf_id, lf_wid);
a998dd70
CV
559 RtlInitCountedUnicodeString (&uname, name,
560 LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
561 InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, lf_inode->i_dir,
c46d7be9 562 everyone_sd (FLOCK_EVENT_ACCESS));
abbde487 563 status = NtCreateEvent (&lf_obj, CYG_EVENT_ACCESS, &attr,
a998dd70
CV
564 NotificationEvent, FALSE);
565 if (!NT_SUCCESS (status))
c46d7be9 566 api_fatal ("NtCreateEvent(lock): %p", status);
a998dd70
CV
567}
568
569/* Open a lock event object for SYNCHRONIZE access (to wait for it). */
636c94d8
CV
570bool
571lockf_t::open_lock_obj ()
a998dd70 572{
636c94d8 573 WCHAR name[LOCK_OBJ_NAME_LEN + 1];
a998dd70
CV
574 UNICODE_STRING uname;
575 OBJECT_ATTRIBUTES attr;
576 NTSTATUS status;
a998dd70 577
636c94d8 578 __small_swprintf (name, L"%02x-%01x-%016X-%016X-%016X-%08x",
072030c3
CV
579 lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start,
580 lf_end, lf_id, lf_wid);
a998dd70
CV
581 RtlInitCountedUnicodeString (&uname, name,
582 LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
31390e4c 583 InitializeObjectAttributes (&attr, &uname, 0, lf_inode->i_dir, NULL);
636c94d8 584 status = NtOpenEvent (&lf_obj, FLOCK_EVENT_ACCESS, &attr);
a998dd70 585 if (!NT_SUCCESS (status))
c46d7be9
CV
586 {
587 SetLastError (RtlNtStatusToDosError (status));
636c94d8 588 lf_obj = NULL; /* Paranoia... */
a998dd70 589 }
636c94d8 590 return lf_obj != NULL;
a998dd70
CV
591}
592
593/* Close a lock event handle. The important thing here is to signal it
594 before closing the handle. This way all threads waiting for this
595 lock can wake up. */
596void
636c94d8 597lockf_t::del_lock_obj (HANDLE fhdl, bool signal)
a998dd70
CV
598{
599 if (lf_obj)
600 {
636c94d8
CV
601 /* Only signal the event if it's either a POSIX lock, or, in case of
602 BSD flock locks, if it's an explicit unlock or if the calling fhandler
603 holds the last reference to the file table entry. The file table
604 entry in UNIX terms is equivalent to the FILE_OBJECT in Windows NT
605 terms. It's what the handle/descriptor references when calling
606 CreateFile/open. Calling DuplicateHandle/dup only creates a new
607 handle/descriptor to the same FILE_OBJECT/file table entry. */
608 if ((lf_flags & F_POSIX) || signal
609 || (fhdl && get_obj_handle_count (fhdl) <= 1))
610 SetEvent (lf_obj);
a998dd70
CV
611 NtClose (lf_obj);
612 lf_obj = NULL;
613 }
614}
615
616lockf_t::~lockf_t ()
617{
636c94d8 618 del_lock_obj (NULL);
a998dd70
CV
619}
620
621/*
622 * This variable controls the maximum number of processes that will
623 * be checked in doing deadlock detection.
624 */
636c94d8 625#ifndef __CYGWIN__
a998dd70
CV
626#define MAXDEPTH 50
627static int maxlockdepth = MAXDEPTH;
628#endif
629
630#define NOLOCKF (struct lockf_t *)0
631#define SELF 0x1
632#define OTHERS 0x2
636c94d8 633static int lf_clearlock (lockf_t *, lockf_t **, HANDLE);
a998dd70 634static int lf_findoverlap (lockf_t *, lockf_t *, int, lockf_t ***, lockf_t **);
31390e4c 635static lockf_t *lf_getblock (lockf_t *, inode_t *node);
a998dd70 636static int lf_getlock (lockf_t *, inode_t *, struct __flock64 *);
636c94d8 637static int lf_setlock (lockf_t *, inode_t *, lockf_t **, HANDLE);
a998dd70 638static void lf_split (lockf_t *, lockf_t *, lockf_t **);
636c94d8 639static void lf_wakelock (lockf_t *, HANDLE);
a998dd70
CV
640
641int
642fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
643{
644 _off64_t start, end, oadd;
645 lockf_t *n;
646 int error = 0;
a998dd70
CV
647
648 short a_flags = fl->l_type & (F_POSIX | F_FLOCK);
649 short type = fl->l_type & (F_RDLCK | F_WRLCK | F_UNLCK);
025c1fac 650
a998dd70
CV
651 if (!a_flags)
652 a_flags = F_POSIX; /* default */
653 if (a_op == F_SETLKW)
654 {
655 a_op = F_SETLK;
656 a_flags |= F_WAIT;
657 }
658 if (a_op == F_SETLK)
659 switch (type)
660 {
661 case F_UNLCK:
662 a_op = F_UNLCK;
663 break;
664 case F_RDLCK:
fc69f1ae
CV
665 /* flock semantics don't specify a requirement that the file has
666 been opened with a specific open mode, in contrast to POSIX locks
667 which require that a file is opened for reading to place a read
668 lock and opened for writing to place a write lock. */
669 if ((a_flags & F_POSIX) && !(get_access () & GENERIC_READ))
a998dd70
CV
670 {
671 set_errno (EBADF);
672 return -1;
673 }
674 break;
675 case F_WRLCK:
fc69f1ae
CV
676 /* See above comment. */
677 if ((a_flags & F_POSIX) && !(get_access () & GENERIC_WRITE))
a998dd70
CV
678 {
679 set_errno (EBADF);
680 return -1;
681 }
682 break;
683 default:
025c1fac 684 set_errno (EINVAL);
a998dd70
CV
685 return -1;
686 }
687
a998dd70
CV
688 /*
689 * Convert the flock structure into a start and end.
690 */
691 switch (fl->l_whence)
692 {
693 case SEEK_SET:
694 start = fl->l_start;
695 break;
696
697 case SEEK_CUR:
698 if ((start = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK)
699 return -1;
700 break;
701
702 case SEEK_END:
636c94d8
CV
703 {
704 NTSTATUS status;
705 IO_STATUS_BLOCK io;
706 FILE_STANDARD_INFORMATION fsi;
707
708 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
709 FileStandardInformation);
710 if (!NT_SUCCESS (status))
711 {
712 __seterrno_from_nt_status (status);
713 return -1;
714 }
715 if (fl->l_start > 0 && fsi.EndOfFile.QuadPart > OFF_MAX - fl->l_start)
716 {
717 set_errno (EOVERFLOW);
718 return -1;
719 }
720 start = fsi.EndOfFile.QuadPart + fl->l_start;
721 }
a998dd70
CV
722 break;
723
724 default:
725 return (EINVAL);
726 }
727 if (start < 0)
728 {
729 set_errno (EINVAL);
730 return -1;
731 }
732 if (fl->l_len < 0)
733 {
734 if (start == 0)
735 {
736 set_errno (EINVAL);
737 return -1;
738 }
739 end = start - 1;
740 start += fl->l_len;
741 if (start < 0)
742 {
743 set_errno (EINVAL);
744 return -1;
745 }
746 }
747 else if (fl->l_len == 0)
748 end = -1;
749 else
750 {
751 oadd = fl->l_len - 1;
752 if (oadd > OFF_MAX - start)
753 {
754 set_errno (EOVERFLOW);
755 return -1;
756 }
757 end = start + oadd;
758 }
759
636c94d8 760 inode_t *node = inode_t::get (get_dev (), get_ino (), true);
a998dd70
CV
761 if (!node)
762 {
636c94d8
CV
763 set_errno (ENOLCK);
764 return -1;
a998dd70 765 }
636c94d8
CV
766 need_fork_fixup (true);
767
fabfb1a1
CV
768 /* Unlock the fd table which has been locked in fcntl_worker/lock_worker,
769 otherwise a blocking F_SETLKW never wakes up on a signal. */
a998dd70
CV
770 cygheap->fdtab.unlock ();
771
772 lockf_t **head = &node->i_lockf;
773
9fa63f7a 774#if 0
a998dd70
CV
775 /*
776 * Avoid the common case of unlocking when inode_t has no locks.
9fa63f7a
CV
777 *
778 * This shortcut is invalid for Cygwin because the above inode_t::get
779 * call returns with an empty lock list if this process has no locks
780 * on the file yet.
a998dd70
CV
781 */
782 if (*head == NULL)
783 {
784 if (a_op != F_SETLK)
785 {
636c94d8 786 node->UNLOCK ();
a998dd70
CV
787 fl->l_type = F_UNLCK;
788 return 0;
789 }
790 }
9fa63f7a 791#endif
a998dd70
CV
792 /*
793 * Allocate a spare structure in case we have to split.
794 */
795 lockf_t *clean = NULL;
796 if (a_op == F_SETLK || a_op == F_UNLCK)
31390e4c
CV
797 {
798 clean = new lockf_t ();
799 if (!clean)
025c1fac 800 {
636c94d8 801 node->UNLOCK ();
31390e4c
CV
802 set_errno (ENOLCK);
803 return -1;
804 }
805 }
a998dd70
CV
806 /*
807 * Create the lockf_t structure
808 */
809 lockf_t *lock = new lockf_t (node, head, a_flags, type, start, end,
636c94d8
CV
810 (a_flags & F_FLOCK) ? get_unique_id ()
811 : getpid (),
812 myself->dwProcessId);
31390e4c
CV
813 if (!lock)
814 {
636c94d8 815 node->UNLOCK ();
31390e4c
CV
816 set_errno (ENOLCK);
817 return -1;
818 }
a998dd70 819
a998dd70
CV
820 switch (a_op)
821 {
822 case F_SETLK:
636c94d8 823 error = lf_setlock (lock, node, &clean, get_handle ());
a998dd70
CV
824 break;
825
826 case F_UNLCK:
636c94d8 827 error = lf_clearlock (lock, &clean, get_handle ());
a998dd70 828 lock->lf_next = clean;
025c1fac 829 clean = lock;
a998dd70 830 break;
025c1fac 831
a998dd70
CV
832 case F_GETLK:
833 error = lf_getlock (lock, node, fl);
834 lock->lf_next = clean;
835 clean = lock;
836 break;
837
838 default:
839 lock->lf_next = clean;
840 clean = lock;
841 error = EINVAL;
842 break;
843 }
844 for (lock = clean; lock != NULL; )
845 {
846 n = lock->lf_next;
636c94d8 847 lock->del_lock_obj (get_handle (), a_op == F_UNLCK);
a998dd70
CV
848 delete lock;
849 lock = n;
850 }
a74869c0 851 if (node->i_lockf == NULL && !node->waiting ())
636c94d8
CV
852 {
853 INODE_LIST_LOCK ();
854 LIST_REMOVE (node, i_next);
855 node->UNLOCK ();
856 delete node;
857 INODE_LIST_UNLOCK ();
858 }
859 else
860 node->UNLOCK ();
a998dd70
CV
861 if (error)
862 {
863 set_errno (error);
864 return -1;
865 }
866 return 0;
867}
868
869/*
870 * Set a byte-range lock.
871 */
872static int
636c94d8 873lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
025c1fac 874{
a998dd70
CV
875 lockf_t *block;
876 lockf_t **head = lock->lf_head;
877 lockf_t **prev, *overlap;
878 int ovcase, priority, old_prio, needtolink;
30bbc55e 879 tmp_pathbuf tp;
a998dd70
CV
880
881 /*
882 * Set the priority
883 */
884 priority = old_prio = GetThreadPriority (GetCurrentThread ());
885 if (lock->lf_type == F_WRLCK && priority <= THREAD_PRIORITY_ABOVE_NORMAL)
886 priority = THREAD_PRIORITY_HIGHEST;
887 /*
888 * Scan lock list for this file looking for locks that would block us.
889 */
31390e4c 890 /* Create temporary space for the all locks list. */
30bbc55e 891 node->i_all_lf = (lockf_t *) (void *) tp.w_get ();
a998dd70
CV
892 while ((block = lf_getblock(lock, node)))
893 {
636c94d8
CV
894 DWORD ret;
895 HANDLE obj = block->lf_obj;
896 block->lf_obj = NULL;
897
a998dd70
CV
898 /*
899 * Free the structure and return if nonblocking.
900 */
901 if ((lock->lf_flags & F_WAIT) == 0)
902 {
a998dd70
CV
903 lock->lf_next = *clean;
904 *clean = lock;
636c94d8
CV
905 if (obj)
906 NtClose (obj);
a998dd70
CV
907 return EAGAIN;
908 }
909 /*
910 * We are blocked. Since flock style locks cover
911 * the whole file, there is no chance for deadlock.
912 * For byte-range locks we must check for deadlock.
913 *
914 * Deadlock detection is done by looking through the
915 * wait channels to see if there are any cycles that
916 * involve us. MAXDEPTH is set just to make sure we
917 * do not go off into neverland.
918 */
919 /* FIXME: We check the handle count of all the lock event objects
025c1fac 920 this process holds. If it's > 1, another process is
a998dd70
CV
921 waiting for one of our locks. This method isn't overly
922 intelligent. If it turns out to be too dumb, we might
923 have to remove it or to find another method. */
924 for (lockf_t *lk = node->i_lockf; lk; lk = lk->lf_next)
636c94d8
CV
925 if ((lk->lf_flags & F_POSIX) && get_obj_handle_count (lk->lf_obj) > 1)
926 {
927 if (obj)
928 NtClose (obj);
929 return EDEADLK;
930 }
a998dd70
CV
931
932 /*
933 * For flock type locks, we must first remove
934 * any shared locks that we hold before we sleep
935 * waiting for an exclusive lock.
936 */
937 if ((lock->lf_flags & F_FLOCK) && lock->lf_type == F_WRLCK)
938 {
939 lock->lf_type = F_UNLCK;
636c94d8 940 (void) lf_clearlock (lock, clean, fhdl);
a998dd70
CV
941 lock->lf_type = F_WRLCK;
942 }
943
944 /*
945 * Add our lock to the blocked list and sleep until we're free.
946 * Remember who blocked us (for deadlock detection).
947 */
636c94d8 948 /* Cygwin: No locked list. See deadlock recognition above. */
a998dd70
CV
949
950 /* Wait for the blocking object and its holding process. */
c46d7be9
CV
951 if (!obj)
952 {
953 /* We can't synchronize on the lock event object.
954 Treat this as a deadlock-like situation for now. */
955 system_printf ("Can't sync with lock object hold by "
956 "Win32 pid %lu: %E", block->lf_wid);
c46d7be9
CV
957 return EDEADLK;
958 }
636c94d8
CV
959 SetThreadPriority (GetCurrentThread (), priority);
960 if (lock->lf_flags & F_POSIX)
025c1fac 961 {
636c94d8
CV
962 HANDLE proc = OpenProcess (SYNCHRONIZE, FALSE, block->lf_wid);
963 if (!proc)
964 {
965 /* If we can't synchronize on the process holding the lock,
966 we will never recognize when the lock has been abandoned.
967 Treat this as a deadlock-like situation for now. */
968 system_printf ("Can't sync with process holding a lock "
969 "(Win32 pid %lu): %E", block->lf_wid);
970 NtClose (obj);
971 return EDEADLK;
972 }
973 HANDLE w4[3] = { obj, proc, signal_arrived };
a74869c0 974 node->wait ();
636c94d8
CV
975 node->UNLOCK ();
976 ret = WaitForMultipleObjects (3, w4, FALSE, INFINITE);
977 CloseHandle (proc);
978 }
979 else
025c1fac 980 {
636c94d8 981 HANDLE w4[2] = { obj, signal_arrived };
a74869c0 982 node->wait ();
636c94d8
CV
983 node->UNLOCK ();
984 /* Unfortunately, since BSD flock locks are not attached to a
985 specific process, we can't recognize an abandoned lock by
986 sync'ing with a process. We have to find out if we're the only
987 process left accessing this event object. */
988 do
989 {
990 ret = WaitForMultipleObjects (2, w4, FALSE, 100L);
991 }
992 while (ret == WAIT_TIMEOUT && get_obj_handle_count (obj) > 1);
5698171a
CV
993 /* There's a good chance that the above loop is left with
994 ret == WAIT_TIMEOUT if another process closes the file handle
995 associated with this lock. This is for all practical purposes
996 equivalent to a signalled lock object. */
997 if (ret == WAIT_TIMEOUT)
998 ret = WAIT_OBJECT_0;
b9e2579c 999 }
a998dd70 1000 node->LOCK ();
a74869c0 1001 node->unwait ();
636c94d8
CV
1002 NtClose (obj);
1003 SetThreadPriority (GetCurrentThread (), old_prio);
a998dd70
CV
1004 switch (ret)
1005 {
1006 case WAIT_OBJECT_0:
636c94d8 1007 /* The lock object has been set to signalled. */
a998dd70 1008 break;
636c94d8 1009 case WAIT_OBJECT_0 + 1:
025c1fac 1010 /* For POSIX locks, the process holding the lock has exited. */
636c94d8
CV
1011 if (lock->lf_flags & F_POSIX)
1012 break;
1013 /*FALLTHRU*/
a998dd70
CV
1014 case WAIT_OBJECT_0 + 2:
1015 /* A signal came in. */
1016 _my_tls.call_signal_handler ();
1017 return EINTR;
1018 default:
5698171a
CV
1019 system_printf ("Shouldn't happen! ret = %lu, error: %lu\n",
1020 ret, GetLastError ());
a998dd70
CV
1021 return geterrno_from_win_error ();
1022 }
1023 }
a998dd70
CV
1024 allow_others_to_sync ();
1025 /*
1026 * No blocks!! Add the lock. Note that we will
1027 * downgrade or upgrade any overlapping locks this
1028 * process already owns.
1029 *
1030 * Handle any locks that overlap.
1031 */
1032 prev = head;
1033 block = *head;
1034 needtolink = 1;
1035 for (;;)
1036 {
1037 ovcase = lf_findoverlap (block, lock, SELF, &prev, &overlap);
1038 if (ovcase)
1039 block = overlap->lf_next;
1040 /*
1041 * Six cases:
1042 * 0) no overlap
1043 * 1) overlap == lock
1044 * 2) overlap contains lock
1045 * 3) lock contains overlap
1046 * 4) overlap starts before lock
1047 * 5) overlap ends after lock
1048 */
1049 switch (ovcase)
1050 {
025c1fac 1051 case 0: /* no overlap */
a998dd70
CV
1052 if (needtolink)
1053 {
1054 *prev = lock;
1055 lock->lf_next = overlap;
1056 lock->create_lock_obj ();
025c1fac
CF
1057 }
1058 break;
a998dd70 1059
025c1fac 1060 case 1: /* overlap == lock */
a998dd70
CV
1061 /*
1062 * If downgrading lock, others may be
1063 * able to acquire it.
1064 * Cygwin: Always wake lock.
1065 */
636c94d8 1066 lf_wakelock (overlap, fhdl);
a998dd70
CV
1067 overlap->lf_type = lock->lf_type;
1068 overlap->create_lock_obj ();
1069 lock->lf_next = *clean;
1070 *clean = lock;
1071 break;
1072
025c1fac 1073 case 2: /* overlap contains lock */
a998dd70
CV
1074 /*
1075 * Check for common starting point and different types.
1076 */
1077 if (overlap->lf_type == lock->lf_type)
1078 {
1079 lock->lf_next = *clean;
1080 *clean = lock;
1081 break;
1082 }
1083 if (overlap->lf_start == lock->lf_start)
1084 {
1085 *prev = lock;
1086 lock->lf_next = overlap;
1087 overlap->lf_start = lock->lf_end + 1;
1088 }
1089 else
1090 lf_split (overlap, lock, clean);
636c94d8 1091 lf_wakelock (overlap, fhdl);
a998dd70
CV
1092 overlap->create_lock_obj ();
1093 lock->create_lock_obj ();
1094 if (lock->lf_next && !lock->lf_next->lf_obj)
1095 lock->lf_next->create_lock_obj ();
1096 break;
1097
025c1fac 1098 case 3: /* lock contains overlap */
a998dd70
CV
1099 /*
1100 * If downgrading lock, others may be able to
1101 * acquire it, otherwise take the list.
1102 * Cygwin: Always wake old lock and create new lock.
1103 */
636c94d8 1104 lf_wakelock (overlap, fhdl);
a998dd70
CV
1105 /*
1106 * Add the new lock if necessary and delete the overlap.
1107 */
1108 if (needtolink)
1109 {
1110 *prev = lock;
1111 lock->lf_next = overlap->lf_next;
1112 prev = &lock->lf_next;
1113 lock->create_lock_obj ();
1114 needtolink = 0;
1115 }
1116 else
1117 *prev = overlap->lf_next;
1118 overlap->lf_next = *clean;
1119 *clean = overlap;
1120 continue;
1121
025c1fac 1122 case 4: /* overlap starts before lock */
a998dd70
CV
1123 /*
1124 * Add lock after overlap on the list.
1125 */
1126 lock->lf_next = overlap->lf_next;
1127 overlap->lf_next = lock;
1128 overlap->lf_end = lock->lf_start - 1;
1129 prev = &lock->lf_next;
636c94d8 1130 lf_wakelock (overlap, fhdl);
a998dd70
CV
1131 overlap->create_lock_obj ();
1132 lock->create_lock_obj ();
1133 needtolink = 0;
1134 continue;
1135
025c1fac 1136 case 5: /* overlap ends after lock */
a998dd70
CV
1137 /*
1138 * Add the new lock before overlap.
1139 */
1140 if (needtolink) {
1141 *prev = lock;
1142 lock->lf_next = overlap;
1143 }
1144 overlap->lf_start = lock->lf_end + 1;
636c94d8 1145 lf_wakelock (overlap, fhdl);
a998dd70
CV
1146 lock->create_lock_obj ();
1147 overlap->create_lock_obj ();
1148 break;
1149 }
1150 break;
1151 }
1152 return 0;
1153}
1154
1155/*
1156 * Remove a byte-range lock on an inode_t.
1157 *
1158 * Generally, find the lock (or an overlap to that lock)
1159 * and remove it (or shrink it), then wakeup anyone we can.
1160 */
1161static int
636c94d8 1162lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE fhdl)
a998dd70
CV
1163{
1164 lockf_t **head = unlock->lf_head;
1165 lockf_t *lf = *head;
1166 lockf_t *overlap, **prev;
1167 int ovcase;
1168
1169 if (lf == NOLOCKF)
1170 return 0;
1171 prev = head;
1172 while ((ovcase = lf_findoverlap (lf, unlock, SELF, &prev, &overlap)))
1173 {
1174 /*
1175 * Wakeup the list of locks to be retried.
1176 */
636c94d8 1177 lf_wakelock (overlap, fhdl);
a998dd70
CV
1178
1179 switch (ovcase)
1180 {
025c1fac 1181 case 1: /* overlap == lock */
a998dd70
CV
1182 *prev = overlap->lf_next;
1183 overlap->lf_next = *clean;
1184 *clean = overlap;
1185 break;
1186
025c1fac 1187 case 2: /* overlap contains lock: split it */
a998dd70
CV
1188 if (overlap->lf_start == unlock->lf_start)
1189 {
1190 overlap->lf_start = unlock->lf_end + 1;
1191 overlap->create_lock_obj ();
1192 break;
1193 }
1194 lf_split (overlap, unlock, clean);
1195 overlap->lf_next = unlock->lf_next;
1196 overlap->create_lock_obj ();
1197 if (overlap->lf_next && !overlap->lf_next->lf_obj)
1198 overlap->lf_next->create_lock_obj ();
1199 break;
1200
025c1fac 1201 case 3: /* lock contains overlap */
a998dd70
CV
1202 *prev = overlap->lf_next;
1203 lf = overlap->lf_next;
1204 overlap->lf_next = *clean;
1205 *clean = overlap;
1206 continue;
1207
025c1fac
CF
1208 case 4: /* overlap starts before lock */
1209 overlap->lf_end = unlock->lf_start - 1;
1210 prev = &overlap->lf_next;
1211 lf = overlap->lf_next;
a998dd70 1212 overlap->create_lock_obj ();
025c1fac 1213 continue;
a998dd70 1214
025c1fac
CF
1215 case 5: /* overlap ends after lock */
1216 overlap->lf_start = unlock->lf_end + 1;
a998dd70 1217 overlap->create_lock_obj ();
025c1fac 1218 break;
a998dd70
CV
1219 }
1220 break;
1221 }
1222 return 0;
1223}
1224
1225/*
1226 * Check whether there is a blocking lock,
1227 * and if so return its process identifier.
1228 */
1229static int
1230lf_getlock (lockf_t *lock, inode_t *node, struct __flock64 *fl)
1231{
1232 lockf_t *block;
30bbc55e 1233 tmp_pathbuf tp;
a998dd70 1234
31390e4c 1235 /* Create temporary space for the all locks list. */
30bbc55e 1236 node->i_all_lf = (lockf_t *) (void * ) tp.w_get ();
a998dd70
CV
1237 if ((block = lf_getblock (lock, node)))
1238 {
636c94d8
CV
1239 if (block->lf_obj)
1240 NtClose (block->lf_obj);
a998dd70
CV
1241 fl->l_type = block->lf_type;
1242 fl->l_whence = SEEK_SET;
1243 fl->l_start = block->lf_start;
1244 if (block->lf_end == -1)
1245 fl->l_len = 0;
1246 else
1247 fl->l_len = block->lf_end - block->lf_start + 1;
1248 if (block->lf_flags & F_POSIX)
636c94d8 1249 fl->l_pid = (pid_t) block->lf_id;
a998dd70
CV
1250 else
1251 fl->l_pid = -1;
1252 }
1253 else
1254 fl->l_type = F_UNLCK;
a998dd70
CV
1255 return 0;
1256}
1257
1258/*
1259 * Walk the list of locks for an inode_t and
1260 * return the first blocking lock.
1261 */
1262static lockf_t *
1263lf_getblock (lockf_t *lock, inode_t *node)
025c1fac 1264{
31390e4c
CV
1265 lockf_t **prev, *overlap;
1266 lockf_t *lf = node->get_all_locks_list ();
a998dd70 1267 int ovcase;
636c94d8
CV
1268 NTSTATUS status;
1269 EVENT_BASIC_INFORMATION ebi;
a998dd70
CV
1270
1271 prev = lock->lf_head;
1272 while ((ovcase = lf_findoverlap (lf, lock, OTHERS, &prev, &overlap)))
1273 {
1274 /*
1275 * We've found an overlap, see if it blocks us
1276 */
1277 if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK))
636c94d8
CV
1278 {
1279 /* Open the event object for synchronization. */
1280 if (!overlap->open_lock_obj () || (overlap->lf_flags & F_POSIX))
1281 return overlap;
1282 /* In case of BSD flock locks, check if the event object is
1283 signalled. If so, the overlap doesn't actually exist anymore.
1284 There are just a few open handles left. */
1285 status = NtQueryEvent (overlap->lf_obj, EventBasicInformation,
1286 &ebi, sizeof ebi, NULL);
1287 if (!NT_SUCCESS (status) || ebi.SignalState == 0)
1288 return overlap;
1289 NtClose (overlap->lf_obj);
1290 overlap->lf_obj = NULL;
1291 }
a998dd70
CV
1292 /*
1293 * Nope, point to the next one on the list and
1294 * see if it blocks us
1295 */
1296 lf = overlap->lf_next;
1297 }
1298 return NOLOCKF;
1299}
1300
1301/*
1302 * Walk the list of locks for an inode_t to
1303 * find an overlapping lock (if any).
1304 *
1305 * NOTE: this returns only the FIRST overlapping lock. There
1306 * may be more than one.
025c1fac 1307 */
a998dd70
CV
1308static int
1309lf_findoverlap (lockf_t *lf, lockf_t *lock, int type, lockf_t ***prev,
1310 lockf_t **overlap)
1311{
1312 _off64_t start, end;
1313
1314 *overlap = lf;
1315 if (lf == NOLOCKF)
1316 return 0;
1317
1318 start = lock->lf_start;
1319 end = lock->lf_end;
1320 while (lf != NOLOCKF)
1321 {
a74869c0
CV
1322 if (((type & SELF) && lf->lf_id != lock->lf_id)
1323 || ((type & OTHERS) && lf->lf_id == lock->lf_id)
636c94d8 1324 /* As on Linux: POSIX locks and BSD flock locks don't interact. */
072030c3
CV
1325 || (lf->lf_flags & (F_POSIX | F_FLOCK))
1326 != (lock->lf_flags & (F_POSIX | F_FLOCK)))
a998dd70
CV
1327 {
1328 *prev = &lf->lf_next;
1329 *overlap = lf = lf->lf_next;
1330 continue;
025c1fac 1331 }
a998dd70
CV
1332 /*
1333 * OK, check for overlap
1334 *
1335 * Six cases:
1336 * 0) no overlap
1337 * 1) overlap == lock
1338 * 2) overlap contains lock
1339 * 3) lock contains overlap
1340 * 4) overlap starts before lock
1341 * 5) overlap ends after lock
1342 */
1343 if ((lf->lf_end != -1 && start > lf->lf_end) ||
1344 (end != -1 && lf->lf_start > end))
1345 {
1346 /* Case 0 */
1347 if ((type & SELF) && end != -1 && lf->lf_start > end)
1348 return 0;
1349 *prev = &lf->lf_next;
1350 *overlap = lf = lf->lf_next;
1351 continue;
025c1fac 1352 }
a998dd70
CV
1353 if ((lf->lf_start == start) && (lf->lf_end == end))
1354 {
1355 /* Case 1 */
1356 return 1;
1357 }
1358 if ((lf->lf_start <= start) && (end != -1) &&
1359 ((lf->lf_end >= end) || (lf->lf_end == -1)))
1360 {
1361 /* Case 2 */
1362 return 2;
1363 }
1364 if (start <= lf->lf_start && (end == -1 ||
1365 (lf->lf_end != -1 && end >= lf->lf_end)))
1366 {
1367 /* Case 3 */
1368 return 3;
1369 }
1370 if ((lf->lf_start < start) &&
1371 ((lf->lf_end >= start) || (lf->lf_end == -1)))
1372 {
1373 /* Case 4 */
1374 return 4;
1375 }
1376 if ((lf->lf_start > start) && (end != -1) &&
1377 ((lf->lf_end > end) || (lf->lf_end == -1)))
1378 {
1379 /* Case 5 */
1380 return 5;
1381 }
1382 api_fatal ("lf_findoverlap: default\n");
1383 }
1384 return 0;
1385}
1386
1387/*
1388 * Split a lock and a contained region into
1389 * two or three locks as necessary.
1390 */
1391static void
1392lf_split (lockf_t *lock1, lockf_t *lock2, lockf_t **split)
1393{
1394 lockf_t *splitlock;
1395
025c1fac 1396 /*
a998dd70
CV
1397 * Check to see if spliting into only two pieces.
1398 */
1399 if (lock1->lf_start == lock2->lf_start)
1400 {
1401 lock1->lf_start = lock2->lf_end + 1;
1402 lock2->lf_next = lock1;
1403 return;
1404 }
1405 if (lock1->lf_end == lock2->lf_end)
1406 {
1407 lock1->lf_end = lock2->lf_start - 1;
1408 lock2->lf_next = lock1->lf_next;
1409 lock1->lf_next = lock2;
1410 return;
1411 }
1412 /*
1413 * Make a new lock consisting of the last part of
1414 * the encompassing lock. We use the preallocated
1415 * splitlock so we don't have to block.
1416 */
1417 splitlock = *split;
1418 assert (splitlock != NULL);
1419 *split = splitlock->lf_next;
1420 memcpy (splitlock, lock1, sizeof *splitlock);
1421 /* We have to unset the obj HANDLE here which has been copied by the
1422 above memcpy, so that the calling function recognizes the new object.
1423 See post-lf_split handling in lf_setlock and lf_clearlock. */
1424 splitlock->lf_obj = NULL;
1425 splitlock->lf_start = lock2->lf_end + 1;
1426 lock1->lf_end = lock2->lf_start - 1;
1427 /*
1428 * OK, now link it in
1429 */
1430 splitlock->lf_next = lock1->lf_next;
1431 lock2->lf_next = splitlock;
1432 lock1->lf_next = lock2;
1433}
1434
1435/*
1436 * Wakeup a blocklist
1437 * Cygwin: Just signal the lock which gets removed. This unblocks
1438 * all threads waiting for this lock.
1439 */
1440static void
636c94d8 1441lf_wakelock (lockf_t *listhead, HANDLE fhdl)
a998dd70 1442{
636c94d8 1443 listhead->del_lock_obj (fhdl, true);
a998dd70 1444}
f3a1e23e 1445
fabfb1a1 1446extern "C" int
f3a1e23e
CV
1447flock (int fd, int operation)
1448{
fabfb1a1
CV
1449 int res = -1;
1450 int cmd;
1451 struct __flock64 fl = { 0, SEEK_SET, 0, 0, 0 };
1452
1453 myfault efault;
1454 if (efault.faulted (EFAULT))
1455 return -1;
1456
1457 cygheap_fdget cfd (fd, true);
1458 if (cfd < 0)
1459 goto done;
1460
1461 cmd = (operation & LOCK_NB) ? F_SETLK : F_SETLKW;
f3a1e23e
CV
1462 switch (operation & (~LOCK_NB))
1463 {
1464 case LOCK_EX:
fabfb1a1 1465 fl.l_type = F_WRLCK | F_FLOCK;
f3a1e23e
CV
1466 break;
1467 case LOCK_SH:
fabfb1a1 1468 fl.l_type = F_RDLCK | F_FLOCK;
f3a1e23e
CV
1469 break;
1470 case LOCK_UN:
fabfb1a1 1471 fl.l_type = F_UNLCK | F_FLOCK;
f3a1e23e
CV
1472 break;
1473 default:
e2a39e2e 1474 set_errno (EINVAL);
fabfb1a1 1475 goto done;
f3a1e23e 1476 }
fabfb1a1 1477 res = cfd->lock (cmd, &fl);
7b9e380f 1478 if ((res == -1) && ((get_errno () == EAGAIN) || (get_errno () == EACCES)))
fabfb1a1
CV
1479 set_errno (EWOULDBLOCK);
1480done:
1481 syscall_printf ("%d = flock (%d, %d)", res, fd, operation);
1482 return res;
f3a1e23e
CV
1483}
1484
a998dd70
CV
1485extern "C" int
1486lockf (int filedes, int function, _off64_t size)
f3a1e23e 1487{
fabfb1a1 1488 int res = -1;
a998dd70 1489 int cmd;
fabfb1a1
CV
1490 struct __flock64 fl;
1491
1492 myfault efault;
1493 if (efault.faulted (EFAULT))
1494 return -1;
1495
1496 cygheap_fdget cfd (filedes, true);
1497 if (cfd < 0)
1498 goto done;
a998dd70
CV
1499
1500 fl.l_start = 0;
1501 fl.l_len = size;
1502 fl.l_whence = SEEK_CUR;
1503
1504 switch (function)
1505 {
1506 case F_ULOCK:
1507 cmd = F_SETLK;
1508 fl.l_type = F_UNLCK;
1509 break;
1510 case F_LOCK:
1511 cmd = F_SETLKW;
1512 fl.l_type = F_WRLCK;
1513 break;
1514 case F_TLOCK:
1515 cmd = F_SETLK;
1516 fl.l_type = F_WRLCK;
1517 break;
1518 case F_TEST:
1519 fl.l_type = F_WRLCK;
fabfb1a1
CV
1520 if (cfd->lock (F_GETLK, &fl) == -1)
1521 goto done;
a998dd70 1522 if (fl.l_type == F_UNLCK || fl.l_pid == getpid ())
fabfb1a1
CV
1523 res = 0;
1524 else
1525 errno = EAGAIN;
1526 goto done;
a998dd70
CV
1527 /* NOTREACHED */
1528 default:
1529 errno = EINVAL;
fabfb1a1 1530 goto done;
a998dd70
CV
1531 /* NOTREACHED */
1532 }
fabfb1a1
CV
1533 res = cfd->lock (cmd, &fl);
1534done:
1535 syscall_printf ("%d = lockf (%d, %d, %D)", res, filedes, function, size);
1536 return res;
f3a1e23e 1537}
This page took 0.389784 seconds and 5 git commands to generate.