1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
|
/* flock.cc. NT specific implementation of advisory file locking.
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* The basic mechanism as well as the datastructures used in the below
implementation are taken from the FreeBSD repository on 2008-03-18.
The essential code of the lf_XXX functions has been taken from the
module src/sys/kern/kern_lockf.c. It has been adapted to use NT
global namespace subdirs and event objects for synchronization
purposes.
So, the following copyright applies to most of the code in the lf_XXX
functions.
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Scooter Morris at Genentech Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_lockf.c 8.3 (Berkeley) 1/6/94
*/
/*
* The flock() function is based upon source taken from the Red Hat
* implementation used in their imap-2002d SRPM.
*
* $RH: flock.c,v 1.2 2000/08/23 17:07:00 nalin Exp $
*/
/* The lockf function is based upon FreeBSD sources with the following
* copyright.
*/
/*
* Copyright (c) 1997 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Klaus Klein.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "winsup.h"
#include <assert.h>
#include <sys/file.h>
#include <unistd.h>
#include <stdlib.h>
#include "cygerrno.h"
#include "security.h"
#include "shared_info.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "pinfo.h"
#include "sigproc.h"
#include "cygtls.h"
#include "tls_pbuf.h"
#include "miscfuncs.h"
#include "ntdll.h"
#include <sys/queue.h>
#include <wchar.h>
#define F_WAIT 0x10 /* Wait until lock is granted */
#define F_FLOCK 0x20 /* Use flock(2) semantics for lock */
#define F_POSIX 0x40 /* Use POSIX semantics for lock */
#ifndef OFF_MAX
#define OFF_MAX LLONG_MAX
#endif
static NO_COPY muto lockf_guard;
#define INODE_LIST_LOCK() (lockf_guard.init ("lockf_guard")->acquire ())
#define INODE_LIST_UNLOCK() (lockf_guard.release ())
#define LOCK_DIR_NAME_FMT L"flock-%08x-%016X"
#define LOCK_DIR_NAME_LEN 31 /* Length of the resulting name */
#define LOCK_DIR_NAME_DEV_OFF 6 /* Offset to device number */
#define LOCK_DIR_NAME_INO_OFF 15 /* Offset to inode number */
/* Don't change format without also changing lockf_t::from_obj_name! */
#define LOCK_OBJ_NAME_FMT L"%02x-%01x-%016X-%016X-%016X-%08x-%04x"
#define LOCK_OBJ_NAME_LEN 69 /* Length of the resulting name */
#define FLOCK_INODE_DIR_ACCESS (DIRECTORY_QUERY \
| DIRECTORY_TRAVERSE \
| DIRECTORY_CREATE_OBJECT \
| READ_CONTROL)
#define FLOCK_EVENT_ACCESS (EVENT_QUERY_STATE \
| SYNCHRONIZE \
| READ_CONTROL)
/* This function takes the own process security descriptor DACL and adds
SYNCHRONIZE permissions for everyone. This allows all processes
to wait for this process to die when blocking in a F_SETLKW on a lock
which is hold by this process. */
static void
allow_others_to_sync ()
{
static NO_COPY bool done;
if (done)
return;
NTSTATUS status;
PACL dacl;
LPVOID ace;
ULONG len;
/* Get this process DACL. We use a rather small stack buffer here which
should be more than sufficient for process ACLs. Can't use tls functions
at this point because this gets called during initialization when the tls
is not really available. */
#define MAX_PROCESS_SD_SIZE 3072
PISECURITY_DESCRIPTOR sd = (PISECURITY_DESCRIPTOR) alloca (MAX_PROCESS_SD_SIZE);
status = NtQuerySecurityObject (NtCurrentProcess (),
DACL_SECURITY_INFORMATION, sd,
MAX_PROCESS_SD_SIZE, &len);
if (!NT_SUCCESS (status))
{
debug_printf ("NtQuerySecurityObject: %y", status);
return;
}
/* Create a valid dacl pointer and set its size to be as big as
there's room in the temporary buffer. Note that the descriptor
is in self-relative format. */
BOOLEAN present, defaulted;
RtlGetDaclSecurityDescriptor (sd, &present, &dacl, &defaulted);
if (!present) /* If so, dacl has undefined value. */
{
dacl = (PACL) (sd + 1);
RtlCreateAcl (dacl, MAX_PROCESS_SD_SIZE - sizeof *sd, ACL_REVISION);
}
else if (dacl == NULL) /* Everyone has all access anyway */
{
done = true;
return;
}
else
{
dacl->AclSize = MAX_PROCESS_SD_SIZE - ((PBYTE) dacl - (PBYTE) sd);
}
/* Allow everyone to SYNCHRONIZE with this process. */
status = RtlAddAccessAllowedAce (dacl, ACL_REVISION, SYNCHRONIZE,
well_known_world_sid);
if (!NT_SUCCESS (status))
{
debug_printf ("RtlAddAccessAllowedAce: %y", status);
return;
}
/* Set the size of the DACL correctly. */
status = RtlFirstFreeAce (dacl, &ace);
if (!NT_SUCCESS (status))
{
debug_printf ("RtlFirstFreeAce: %y", status);
return;
}
dacl->AclSize = (char *) ace - (char *) dacl;
/* Write the DACL back. */
status = NtSetSecurityObject (NtCurrentProcess (), DACL_SECURITY_INFORMATION, sd);
if (!NT_SUCCESS (status))
{
debug_printf ("NtSetSecurityObject: %y", status);
return;
}
done = true;
}
/* Get the handle count of an object. */
static ULONG
get_obj_handle_count (HANDLE h)
{
OBJECT_BASIC_INFORMATION obi;
NTSTATUS status;
ULONG hdl_cnt = 0;
status = NtQueryObject (h, ObjectBasicInformation, &obi, sizeof obi, NULL);
if (!NT_SUCCESS (status))
debug_printf ("NtQueryObject: %y", status);
else
hdl_cnt = obi.HandleCount;
return hdl_cnt;
}
/* Helper struct to construct a local OBJECT_ATTRIBUTES on the stack. */
struct lockfattr_t
{
OBJECT_ATTRIBUTES attr;
UNICODE_STRING uname;
WCHAR name[LOCK_OBJ_NAME_LEN + 1];
};
/* Per lock class. */
class lockf_t
{
public:
uint16_t lf_flags; /* Semantics: F_POSIX, F_FLOCK, F_WAIT */
uint16_t lf_type; /* Lock type: F_RDLCK, F_WRLCK */
off_t lf_start; /* Byte # of the start of the lock */
off_t lf_end; /* Byte # of the end of the lock (-1=EOF) */
int64_t lf_id; /* Cygwin PID for POSIX locks, a unique id per
file table entry for BSD flock locks. */
DWORD lf_wid; /* Win PID of the resource holding the lock */
uint16_t lf_ver; /* Version number of the lock. If a released
lock event yet exists because another process
is still waiting for it, we use the version
field to distinguish old from new locks. */
class lockf_t **lf_head; /* Back pointer to the head of the lockf_t list */
class inode_t *lf_inode; /* Back pointer to the inode_t */
class lockf_t *lf_next; /* Pointer to the next lock on this inode_t */
HANDLE lf_obj; /* Handle to the lock event object. */
lockf_t ()
: lf_flags (0), lf_type (0), lf_start (0), lf_end (0), lf_id (0),
lf_wid (0), lf_ver (0), lf_head (NULL), lf_inode (NULL),
lf_next (NULL), lf_obj (NULL)
{}
lockf_t (class inode_t *node, class lockf_t **head,
short flags, short type, off_t start, off_t end,
long long id, DWORD wid, uint16_t ver)
: lf_flags (flags), lf_type (type), lf_start (start), lf_end (end),
lf_id (id), lf_wid (wid), lf_ver (ver), lf_head (head), lf_inode (node),
lf_next (NULL), lf_obj (NULL)
{}
~lockf_t ();
bool from_obj_name (class inode_t *node, class lockf_t **head,
const wchar_t *name);
/* Used to create all locks list in a given TLS buffer. */
void *operator new (size_t size, void *p)
{ return p; }
/* Used to store own lock list in the cygheap. */
void *operator new (size_t size)
{ return cmalloc (HEAP_FHANDLER, sizeof (lockf_t)); }
/* Never call on node->i_all_lf! */
void operator delete (void *p)
{ cfree (p); }
POBJECT_ATTRIBUTES create_lock_obj_attr (lockfattr_t *attr,
ULONG flags, void *sd_buf);
void create_lock_obj ();
bool open_lock_obj ();
void close_lock_obj () { NtClose (lf_obj); lf_obj = NULL; }
void del_lock_obj (HANDLE fhdl, bool signal = false);
};
/* Per inode_t class */
class inode_t
{
friend class lockf_t;
public:
LIST_ENTRY (inode_t) i_next;
lockf_t *i_lockf; /* List of locks of this process. */
lockf_t *i_all_lf; /* Temp list of all locks for this file. */
dev_t i_dev; /* Device ID */
ino_t i_ino; /* inode number */
private:
HANDLE i_dir;
HANDLE i_mtx;
uint32_t i_cnt; /* # of threads referencing this instance. */
public:
inode_t (dev_t dev, ino_t ino);
~inode_t ();
void *operator new (size_t size)
{ return cmalloc (HEAP_FHANDLER, sizeof (inode_t)); }
void operator delete (void *p)
{ cfree (p); }
static inode_t *get (dev_t dev, ino_t ino,
bool create_if_missing, bool lock);
void LOCK () { WaitForSingleObject (i_mtx, INFINITE); }
void UNLOCK () { ReleaseMutex (i_mtx); }
void use () { ++i_cnt; }
void unuse () { if (i_cnt > 0) --i_cnt; }
bool inuse () { return i_cnt > 0; }
void notused () { i_cnt = 0; }
void unlock_and_remove_if_unused ();
lockf_t *get_all_locks_list ();
bool del_my_locks (long long id, HANDLE fhdl);
};
inode_t::~inode_t ()
{
lockf_t *lock, *n_lock;
for (lock = i_lockf; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
delete lock;
NtClose (i_mtx);
NtClose (i_dir);
}
void
inode_t::unlock_and_remove_if_unused ()
{
UNLOCK ();
INODE_LIST_LOCK ();
unuse ();
if (i_lockf == NULL && !inuse ())
{
LIST_REMOVE (this, i_next);
delete this;
}
INODE_LIST_UNLOCK ();
}
bool
inode_t::del_my_locks (long long id, HANDLE fhdl)
{
lockf_t *lock, *n_lock;
lockf_t **prev = &i_lockf;
for (lock = *prev; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
{
if (lock->lf_flags & F_POSIX)
{
/* Delete all POSIX locks. */
*prev = n_lock;
/* When called during fork, the POSIX lock must get deleted but
*not* signalled. The lock is still active and locked in the
parent. So in case of fork, we call close_lock_obj explicitely,
since del_lock_obj is called from the destructor. */
if (!id)
lock->close_lock_obj ();
delete lock;
}
else if (id && lock->lf_id == id)
{
int cnt = 0;
cygheap_fdenum cfd (true);
while (cfd.next () >= 0)
if (cfd->get_unique_id () == lock->lf_id && ++cnt > 1)
break;
/* Delete BSD flock lock when no other fd in this process references
it anymore. */
if (cnt <= 1)
{
*prev = n_lock;
lock->del_lock_obj (fhdl);
delete lock;
}
}
else
prev = &lock->lf_next;
}
return i_lockf == NULL;
}
/* Used to delete the locks on a file hold by this process. Called from
close(2) and fixup_after_fork, as well as from fixup_after_exec in
case the close_on_exec flag is set. The whole inode is deleted as
soon as no lock exists on it anymore. */
void
fhandler_base::del_my_locks (del_lock_called_from from)
{
inode_t *node = inode_t::get (get_dev (), get_ino (), false, true);
if (node)
{
/* When we're called from fixup_after_exec, the fhandler is a
close-on-exec fhandler. In this case our io handle is already
invalid. We can't use it to test for the object reference count.
However, that shouldn't be necessary for the following reason.
After exec, there are no threads in the current process waiting for
the lock. So, either we're the only process accessing the file table
entry and there are no threads which require signalling, or we have
a parent process still accessing the file object and signalling the
lock event would be premature. */
node->del_my_locks (from == after_fork ? 0 : get_unique_id (),
from == after_exec ? NULL : get_handle ());
node->unlock_and_remove_if_unused ();
}
}
/* Called in an execed child. The exec'ed process must allow SYNCHRONIZE
access to everyone if at least one inode exists.
The lock owner's Windows PID changed and all POSIX lock event objects
have to be relabeled so that waiting processes know which process to
wait on. If the node has been abandoned due to close_on_exec on the
referencing fhandlers, remove the inode entirely. */
void
fixup_lockf_after_exec (bool exec)
{
inode_t *node, *next_node;
INODE_LIST_LOCK ();
if (LIST_FIRST (&cygheap->inode_list))
allow_others_to_sync ();
LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
{
node->notused ();
int cnt = 0;
cygheap_fdenum cfd (true);
while (cfd.next () >= 0)
if (cfd->get_dev () == node->i_dev
&& cfd->get_ino () == node->i_ino
&& ++cnt >= 1)
break;
if (cnt == 0)
{
LIST_REMOVE (node, i_next);
delete node;
}
else
{
node->LOCK ();
lockf_t *lock, *n_lock;
lockf_t **prev = &node->i_lockf;
for (lock = *prev; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
if (lock->lf_flags & F_POSIX)
{
if (exec)
{
/* The parent called exec. The lock is passed to the child.
Recreate lock object with changed ownership. */
lock->del_lock_obj (NULL);
lock->lf_wid = myself->dwProcessId;
lock->lf_ver = 0;
lock->create_lock_obj ();
}
else
{
/* The parent called spawn. The parent continues to hold
the POSIX lock, ownership is not passed to the child.
Give up the lock in the child. */
*prev = n_lock;
lock->close_lock_obj ();
delete lock;
}
}
node->UNLOCK ();
}
}
INODE_LIST_UNLOCK ();
}
/* static method to return a pointer to the inode_t structure for a specific
file. The file is specified by the device and inode_t number. If inode_t
doesn't exist, create it. */
inode_t *
inode_t::get (dev_t dev, ino_t ino, bool create_if_missing, bool lock)
{
inode_t *node;
INODE_LIST_LOCK ();
LIST_FOREACH (node, &cygheap->inode_list, i_next)
if (node->i_dev == dev && node->i_ino == ino)
break;
if (!node && create_if_missing)
{
node = new inode_t (dev, ino);
if (node)
LIST_INSERT_HEAD (&cygheap->inode_list, node, i_next);
}
if (node)
node->use ();
INODE_LIST_UNLOCK ();
if (node && lock)
node->LOCK ();
return node;
}
inode_t::inode_t (dev_t dev, ino_t ino)
: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_cnt (0L)
{
HANDLE parent_dir;
WCHAR name[48];
UNICODE_STRING uname;
OBJECT_ATTRIBUTES attr;
NTSTATUS status;
parent_dir = get_shared_parent_dir ();
/* Create a subdir which is named after the device and inode_t numbers
of the given file, in hex notation. */
int len = __small_swprintf (name, LOCK_DIR_NAME_FMT, dev, ino);
RtlInitCountedUnicodeString (&uname, name, len * sizeof (WCHAR));
InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT | OBJ_OPENIF,
parent_dir, everyone_sd (FLOCK_INODE_DIR_ACCESS));
status = NtCreateDirectoryObject (&i_dir, FLOCK_INODE_DIR_ACCESS, &attr);
if (!NT_SUCCESS (status))
api_fatal ("NtCreateDirectoryObject(inode): %y", status);
/* Create a mutex object in the file specific dir, which is used for
access synchronization on the dir and its objects. */
InitializeObjectAttributes (&attr, &ro_u_mtx, OBJ_INHERIT | OBJ_OPENIF, i_dir,
everyone_sd (CYG_MUTANT_ACCESS));
status = NtCreateMutant (&i_mtx, CYG_MUTANT_ACCESS, &attr, FALSE);
if (!NT_SUCCESS (status))
api_fatal ("NtCreateMutant(inode): %y", status);
}
/* Enumerate all lock event objects for this file and create a lockf_t
list in the i_all_lf member. This list is searched in lf_getblock
for locks which potentially block our lock request. */
/* Number of lockf_t structs which fit in the temporary buffer. */
#define MAX_LOCKF_CNT ((intptr_t)((NT_MAX_PATH * sizeof (WCHAR)) \
/ sizeof (lockf_t)))
bool
lockf_t::from_obj_name (inode_t *node, lockf_t **head, const wchar_t *name)
{
wchar_t *endptr;
/* "%02x-%01x-%016X-%016X-%016X-%08x-%04x",
lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid, lf_ver */
lf_flags = wcstol (name, &endptr, 16);
if ((lf_flags & ~(F_FLOCK | F_POSIX)) != 0
|| ((lf_flags & (F_FLOCK | F_POSIX)) == (F_FLOCK | F_POSIX)))
return false;
lf_type = wcstol (endptr + 1, &endptr, 16);
if ((lf_type != F_RDLCK && lf_type != F_WRLCK) || !endptr || *endptr != L'-')
return false;
lf_start = (off_t) wcstoull (endptr + 1, &endptr, 16);
if (lf_start < 0 || !endptr || *endptr != L'-')
return false;
lf_end = (off_t) wcstoull (endptr + 1, &endptr, 16);
if (lf_end < -1LL
|| (lf_end > 0 && lf_end < lf_start)
|| !endptr || *endptr != L'-')
return false;
lf_id = wcstoll (endptr + 1, &endptr, 16);
if (!endptr || *endptr != L'-'
|| ((lf_flags & F_POSIX) && (lf_id < 1 || lf_id > UINT32_MAX)))
return false;
lf_wid = wcstoul (endptr + 1, &endptr, 16);
if (!endptr || *endptr != L'-')
return false;
lf_ver = wcstoul (endptr + 1, &endptr, 16);
if (endptr && *endptr != L'\0')
return false;
lf_head = head;
lf_inode = node;
lf_next = NULL;
lf_obj = NULL;
return true;
}
lockf_t *
inode_t::get_all_locks_list ()
{
struct fdbi
{
DIRECTORY_BASIC_INFORMATION dbi;
WCHAR buf[2][NAME_MAX + 1];
} f;
ULONG context;
NTSTATUS status;
lockf_t newlock, *lock = i_all_lf;
for (BOOLEAN restart = TRUE;
NT_SUCCESS (status = NtQueryDirectoryObject (i_dir, &f, sizeof f, TRUE,
restart, &context, NULL));
restart = FALSE)
{
if (f.dbi.ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR))
continue;
f.dbi.ObjectName.Buffer[LOCK_OBJ_NAME_LEN] = L'\0';
if (!newlock.from_obj_name (this, &i_all_lf, f.dbi.ObjectName.Buffer))
continue;
if (lock - i_all_lf >= MAX_LOCKF_CNT)
{
system_printf ("Warning, can't handle more than %d locks per file.",
MAX_LOCKF_CNT);
break;
}
if (lock > i_all_lf)
lock[-1].lf_next = lock;
new (lock++) lockf_t (newlock);
}
/* If no lock has been found, return NULL. */
if (lock == i_all_lf)
return NULL;
return i_all_lf;
}
/* Create the lock object name. The name is constructed from the lock
properties which identify it uniquely, all values in hex. */
POBJECT_ATTRIBUTES
lockf_t::create_lock_obj_attr (lockfattr_t *attr, ULONG flags, void *sd_buf)
{
__small_swprintf (attr->name, LOCK_OBJ_NAME_FMT,
lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start, lf_end,
lf_id, lf_wid, lf_ver);
RtlInitCountedUnicodeString (&attr->uname, attr->name,
LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
InitializeObjectAttributes (&attr->attr, &attr->uname, flags, lf_inode->i_dir,
_everyone_sd (sd_buf, FLOCK_EVENT_ACCESS));
return &attr->attr;
}
DWORD WINAPI
create_lock_in_parent (PVOID param)
{
HANDLE lf_obj;
ULONG size;
OBJECT_NAME_INFORMATION *ntfn;
NTSTATUS status;
wchar_t *lockname, *inodename, *endptr;
dev_t dev;
ino_t ino;
inode_t *node;
lockf_t newlock, *lock;
int cnt;
/* param is the handle to the lock object, created by caller. */
lf_obj = (HANDLE) param;
/* Fetch object path from handle. Typically the length of the path
is 146 characters, starting with
"\BaseNamedObject\cygwin-1S5-<16-hex-digits>\..." */
size = sizeof (OBJECT_NAME_INFORMATION) + 256 * sizeof (WCHAR);
ntfn = (OBJECT_NAME_INFORMATION *) alloca (size);
memset (ntfn, 0, size);
status = NtQueryObject (lf_obj, ObjectNameInformation, ntfn, size, &size);
if (!NT_SUCCESS (status))
goto err;
ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = L'\0';
/* Sanity check so that we don't peek into unchartered territory. */
if (ntfn->Name.Length < LOCK_OBJ_NAME_LEN + LOCK_DIR_NAME_LEN + 1)
goto err;
/* The names have fixed size, so we know where the substrings start. */
lockname = ntfn->Name.Buffer + ntfn->Name.Length / sizeof (WCHAR)
- LOCK_OBJ_NAME_LEN;
inodename = lockname - LOCK_DIR_NAME_LEN - 1;
dev = wcstoul (inodename + LOCK_DIR_NAME_DEV_OFF, &endptr, 16);
if (*endptr != L'-')
goto err;
ino = wcstoull (inodename + LOCK_DIR_NAME_INO_OFF, &endptr, 16);
if (*endptr != L'\\')
goto err;
if (!newlock.from_obj_name (NULL, NULL, lockname))
goto err;
/* Check if we have an open file handle with the same unique id. */
{
cnt = 0;
cygheap_fdenum cfd (true);
while (cfd.next () >= 0)
if (cfd->get_unique_id () == newlock.lf_id && ++cnt > 0)
break;
}
/* If not, close handle and return. */
if (!cnt)
{
NtClose (lf_obj);
return 0;
}
/* The handle gets created non-inheritable. That's fine, unless the parent
starts another process accessing this object. So, after it's clear we
have to store the handle for further use, make sure it gets inheritable
by child processes. */
if (!SetHandleInformation (lf_obj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
goto err;
/* otherwise generate inode from directory name... */
node = inode_t::get (dev, ino, true, false);
/* ...and generate lock from object name. */
lock = new lockf_t (newlock);
lock->lf_inode = node;
lock->lf_head = &node->i_lockf;
lock->lf_next = node->i_lockf;
lock->lf_obj = lf_obj;
node->i_lockf = lock;
node->unuse ();
return 0;
err:
system_printf ("Adding <%S> lock failed", &ntfn->Name);
NtClose (lf_obj);
return 1;
}
DWORD WINAPI
delete_lock_in_parent (PVOID param)
{
inode_t *node, *next_node;
lockf_t *lock, **prev;
/* Scan list of all inodes, and reap stale BSD lock if lf_id matches.
Remove inode if empty. */
INODE_LIST_LOCK ();
LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
if (!node->inuse ())
{
for (prev = &node->i_lockf, lock = *prev; lock; lock = *prev)
{
if ((lock->lf_flags & F_FLOCK) && IsEventSignalled (lock->lf_obj))
{
*prev = lock->lf_next;
delete lock;
}
else
prev = &lock->lf_next;
}
if (node->i_lockf == NULL)
{
LIST_REMOVE (node, i_next);
delete node;
}
}
INODE_LIST_UNLOCK ();
return 0;
}
/* Create the lock event object in the file's subdir in the NT global
namespace. */
void
lockf_t::create_lock_obj ()
{
lockfattr_t attr;
NTSTATUS status;
PSECURITY_DESCRIPTOR sd_buf = alloca (SD_MIN_SIZE);
POBJECT_ATTRIBUTES lock_obj_attr;
do
{
lock_obj_attr = create_lock_obj_attr (&attr, OBJ_INHERIT, sd_buf);
status = NtCreateEvent (&lf_obj, CYG_EVENT_ACCESS, lock_obj_attr,
NotificationEvent, FALSE);
if (!NT_SUCCESS (status))
{
if (status != STATUS_OBJECT_NAME_COLLISION)
api_fatal ("NtCreateEvent(lock): %y", status);
/* If we get a STATUS_OBJECT_NAME_COLLISION, the event still exists
because some other process is waiting for it in lf_setlock.
If so, check the event's signal state. If we can't open it, it
has been closed in the meantime, so just try again. If we can
open it and the object is not signalled, it's surely a bug in the
code somewhere. Otherwise, close the event and retry to create
a new event with another name. */
if (open_lock_obj ())
{
if (!IsEventSignalled (lf_obj))
api_fatal ("NtCreateEvent(lock): %y", status);
close_lock_obj ();
/* Increment the lf_ver field until we have no collision. */
++lf_ver;
}
}
}
while (!NT_SUCCESS (status));
/* For BSD locks, notify the parent process. */
if (lf_flags & F_FLOCK)
{
HANDLE parent_proc, parent_thread, parent_lf_obj;
pinfo p (myself->ppid);
if (!p) /* No access or not a Cygwin parent. */
return;
parent_proc = OpenProcess (PROCESS_DUP_HANDLE
| PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_WRITE
| PROCESS_VM_READ,
FALSE, p->dwProcessId);
if (!parent_proc)
{
debug_printf ("OpenProcess (%u): %E", p->dwProcessId);
return;
}
if (!DuplicateHandle (GetCurrentProcess (), lf_obj, parent_proc,
&parent_lf_obj, TRUE, FALSE, DUPLICATE_SAME_ACCESS))
debug_printf ("DuplicateHandle (lf_obj): %E");
else
{
parent_thread = CreateRemoteThread (parent_proc, NULL, 256 * 1024,
create_lock_in_parent,
parent_lf_obj,
STACK_SIZE_PARAM_IS_A_RESERVATION,
NULL);
if (!parent_thread)
{
debug_printf ("CreateRemoteThread: %E");
/* If thread didn't get started, close object handle in parent,
otherwise suffer handle leaks. */
DuplicateHandle (parent_proc, parent_lf_obj, parent_proc,
NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
}
else
{
/* Must wait to avoid race conditions. */
WaitForSingleObject (parent_thread, INFINITE);
CloseHandle (parent_thread);
}
}
CloseHandle (parent_proc);
}
}
/* Open a lock event object for SYNCHRONIZE access (to wait for it). */
bool
lockf_t::open_lock_obj ()
{
lockfattr_t attr;
NTSTATUS status;
status = NtOpenEvent (&lf_obj, FLOCK_EVENT_ACCESS,
create_lock_obj_attr (&attr, 0, alloca (SD_MIN_SIZE)));
if (!NT_SUCCESS (status))
{
SetLastError (RtlNtStatusToDosError (status));
lf_obj = NULL; /* Paranoia... */
}
return lf_obj != NULL;
}
/* Delete a lock event handle. The important thing here is to signal it
before closing the handle. This way all threads waiting for this lock
can wake up. */
void
lockf_t::del_lock_obj (HANDLE fhdl, bool signal)
{
if (lf_obj)
{
/* Only signal the event if it's either a POSIX lock, or, in case of
BSD flock locks, if it's an explicit unlock or if the calling fhandler
holds the last reference to the file table entry. The file table
entry in UNIX terms is equivalent to the FILE_OBJECT in Windows NT
terms. It's what the handle/descriptor references when calling
CreateFile/open. Calling DuplicateHandle/dup only creates a new
handle/descriptor to the same FILE_OBJECT/file table entry. */
if ((lf_flags & F_POSIX) || signal
|| (fhdl && get_obj_handle_count (fhdl) <= 1))
{
NTSTATUS status = NtSetEvent (lf_obj, NULL);
if (!NT_SUCCESS (status))
system_printf ("NtSetEvent, %y", status);
/* For BSD locks, notify the parent process. */
if (lf_flags & F_FLOCK)
{
HANDLE parent_proc, parent_thread;
pinfo p (myself->ppid);
if (p && (parent_proc = OpenProcess (PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_WRITE
| PROCESS_VM_READ,
FALSE, p->dwProcessId)))
{
parent_thread = CreateRemoteThread (parent_proc, NULL,
256 * 1024, delete_lock_in_parent,
NULL,
STACK_SIZE_PARAM_IS_A_RESERVATION,
NULL);
if (parent_thread)
{
/* Must wait to avoid race conditions. */
WaitForSingleObject (parent_thread, INFINITE);
CloseHandle (parent_thread);
}
CloseHandle (parent_proc);
}
}
}
close_lock_obj ();
}
}
lockf_t::~lockf_t ()
{
del_lock_obj (NULL);
}
/*
* This variable controls the maximum number of processes that will
* be checked in doing deadlock detection.
*/
#ifndef __CYGWIN__
#define MAXDEPTH 50
static int maxlockdepth = MAXDEPTH;
#endif
#define NOLOCKF (struct lockf_t *)0
#define SELF 0x1
#define OTHERS 0x2
static int lf_clearlock (lockf_t *, lockf_t **, HANDLE);
static int lf_findoverlap (lockf_t *, lockf_t *, int, lockf_t ***, lockf_t **);
static lockf_t *lf_getblock (lockf_t *, inode_t *node);
static int lf_getlock (lockf_t *, inode_t *, struct flock *);
static int lf_setlock (lockf_t *, inode_t *, lockf_t **, HANDLE);
static void lf_split (lockf_t *, lockf_t *, lockf_t **);
static void lf_wakelock (lockf_t *, HANDLE);
/* This is the fcntl advisory lock implementation. For the implementation
of mandatory locks using the Windows mandatory locking functions, see the
fhandler_disk_file::mand_lock method at the end of this file. */
int
fhandler_base::lock (int a_op, struct flock *fl)
{
off_t start, end, oadd;
int error = 0;
short a_flags = fl->l_type & (F_POSIX | F_FLOCK);
short type = fl->l_type & (F_RDLCK | F_WRLCK | F_UNLCK);
if (!a_flags)
a_flags = F_POSIX; /* default */
/* FIXME: For BSD flock(2) we need a valid, per file table entry OS handle.
Therefore we can't allow using flock(2) on nohandle devices and
pre-Windows 8 console handles (recognized by their odd handle value). */
if ((a_flags & F_FLOCK)
&& (nohandle () || (((uintptr_t) get_handle () & 0x3) == 0x3)))
{
set_errno (EINVAL);
debug_printf ("BSD locking on nohandle and old-style console devices "
"not supported");
return -1;
}
if (a_op == F_SETLKW)
{
a_op = F_SETLK;
a_flags |= F_WAIT;
}
if (a_op == F_SETLK)
switch (type)
{
case F_UNLCK:
a_op = F_UNLCK;
break;
case F_RDLCK:
/* flock semantics don't specify a requirement that the file has
been opened with a specific open mode, in contrast to POSIX locks
which require that a file is opened for reading to place a read
lock and opened for writing to place a write lock. */
/* CV 2013-10-22: Test POSIX R/W mode flags rather than Windows R/W
access flags. The reason is that POSIX mode flags are set for
all types of fhandlers, while Windows access flags are only set
for most of the actual Windows device backed fhandlers. */
if ((a_flags & F_POSIX)
&& ((get_flags () & O_ACCMODE) == O_WRONLY))
{
debug_printf ("request F_RDLCK on O_WRONLY file: EBADF");
set_errno (EBADF);
return -1;
}
break;
case F_WRLCK:
/* See above comment. */
if ((a_flags & F_POSIX)
&& ((get_flags () & O_ACCMODE) == O_RDONLY))
{
debug_printf ("request F_WRLCK on O_RDONLY file: EBADF");
set_errno (EBADF);
return -1;
}
break;
default:
set_errno (EINVAL);
return -1;
}
/*
* Convert the flock structure into a start and end.
*/
switch (fl->l_whence)
{
case SEEK_SET:
start = fl->l_start;
break;
case SEEK_CUR:
if ((start = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK)
start = 0;
break;
case SEEK_END:
if (get_device () != FH_FS)
start = 0;
else
{
NTSTATUS status;
IO_STATUS_BLOCK io;
FILE_STANDARD_INFORMATION fsi;
status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
FileStandardInformation);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
if (fl->l_start > 0 && fsi.EndOfFile.QuadPart > OFF_MAX - fl->l_start)
{
set_errno (EOVERFLOW);
return -1;
}
start = fsi.EndOfFile.QuadPart + fl->l_start;
}
break;
default:
return (EINVAL);
}
if (start < 0)
{
set_errno (EINVAL);
return -1;
}
if (fl->l_len < 0)
{
if (start == 0)
{
set_errno (EINVAL);
return -1;
}
end = start - 1;
start += fl->l_len;
if (start < 0)
{
set_errno (EINVAL);
return -1;
}
}
else if (fl->l_len == 0)
end = -1;
else
{
oadd = fl->l_len - 1;
if (oadd > OFF_MAX - start)
{
set_errno (EOVERFLOW);
return -1;
}
end = start + oadd;
}
restart: /* Entry point after a restartable signal came in. */
inode_t *node = inode_t::get (get_dev (), get_ino (), true, true);
if (!node)
{
set_errno (ENOLCK);
return -1;
}
/* Unlock the fd table which has been locked in fcntl_worker/lock_worker,
otherwise a blocking F_SETLKW never wakes up on a signal. */
cygheap->fdtab.unlock ();
lockf_t **head = &node->i_lockf;
#if 0
/*
* Avoid the common case of unlocking when inode_t has no locks.
*
* This shortcut is invalid for Cygwin because the above inode_t::get
* call returns with an empty lock list if this process has no locks
* on the file yet.
*/
if (*head == NULL)
{
if (a_op != F_SETLK)
{
node->UNLOCK ();
fl->l_type = F_UNLCK;
return 0;
}
}
#endif
/*
* Allocate a spare structure in case we have to split.
*/
lockf_t *clean = NULL;
if (a_op == F_SETLK || a_op == F_UNLCK)
{
clean = new lockf_t ();
if (!clean)
{
node->unlock_and_remove_if_unused ();
set_errno (ENOLCK);
return -1;
}
}
/*
* Create the lockf_t structure
*/
lockf_t *lock = new lockf_t (node, head, a_flags, type, start, end,
(a_flags & F_FLOCK) ? get_unique_id ()
: getpid (),
myself->dwProcessId, 0);
if (!lock)
{
node->unlock_and_remove_if_unused ();
set_errno (ENOLCK);
return -1;
}
switch (a_op)
{
case F_SETLK:
error = lf_setlock (lock, node, &clean, get_handle ());
break;
case F_UNLCK:
error = lf_clearlock (lock, &clean, get_handle ());
lock->lf_next = clean;
clean = lock;
break;
case F_GETLK:
error = lf_getlock (lock, node, fl);
lock->lf_next = clean;
clean = lock;
break;
default:
lock->lf_next = clean;
clean = lock;
error = EINVAL;
break;
}
for (lock = clean; lock != NULL; )
{
lockf_t *n = lock->lf_next;
lock->del_lock_obj (get_handle (), a_op == F_UNLCK);
delete lock;
lock = n;
}
node->unlock_and_remove_if_unused ();
switch (error)
{
case 0: /* All is well. */
need_fork_fixup (true);
return 0;
case EINTR: /* Signal came in. */
if (_my_tls.call_signal_handler ())
goto restart;
break;
case ECANCELED: /* The thread has been sent a cancellation request. */
pthread::static_cancel_self ();
/*NOTREACHED*/
default:
break;
}
set_errno (error);
return -1;
}
/*
* Set a byte-range lock.
*/
static int
lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
{
lockf_t *block;
lockf_t **head = lock->lf_head;
lockf_t **prev, *overlap;
int ovcase, priority, old_prio, needtolink;
tmp_pathbuf tp;
/*
* Set the priority
*/
priority = old_prio = GetThreadPriority (GetCurrentThread ());
if (lock->lf_type == F_WRLCK && priority <= THREAD_PRIORITY_ABOVE_NORMAL)
priority = THREAD_PRIORITY_HIGHEST;
/*
* Scan lock list for this file looking for locks that would block us.
*/
/* Create temporary space for the all locks list. */
node->i_all_lf = (lockf_t *) (void *) tp.w_get ();
while ((block = lf_getblock(lock, node)))
{
HANDLE obj = block->lf_obj;
block->lf_obj = NULL;
/*
* Free the structure and return if nonblocking.
*/
if ((lock->lf_flags & F_WAIT) == 0)
{
NtClose (obj);
lock->lf_next = *clean;
*clean = lock;
return EAGAIN;
}
/*
* We are blocked. Since flock style locks cover
* the whole file, there is no chance for deadlock.
* For byte-range locks we must check for deadlock.
*
* Deadlock detection is done by looking through the
* wait channels to see if there are any cycles that
* involve us. MAXDEPTH is set just to make sure we
* do not go off into neverland.
*/
/* FIXME: We check the handle count of all the lock event objects
this process holds. If it's > 1, another process is
waiting for one of our locks. This method isn't overly
intelligent. If it turns out to be too dumb, we might
have to remove it or to find another method. */
if (lock->lf_flags & F_POSIX)
for (lockf_t *lk = node->i_lockf; lk; lk = lk->lf_next)
if ((lk->lf_flags & F_POSIX) && get_obj_handle_count (lk->lf_obj) > 1)
{
NtClose (obj);
return EDEADLK;
}
/*
* For flock type locks, we must first remove
* any shared locks that we hold before we sleep
* waiting for an exclusive lock.
*/
if ((lock->lf_flags & F_FLOCK) && lock->lf_type == F_WRLCK)
{
lock->lf_type = F_UNLCK;
(void) lf_clearlock (lock, clean, fhdl);
lock->lf_type = F_WRLCK;
}
/*
* Add our lock to the blocked list and sleep until we're free.
* Remember who blocked us (for deadlock detection).
*/
/* Cygwin: No locked list. See deadlock recognition above. */
node->UNLOCK ();
/* Create list of objects to wait for. */
HANDLE w4[4] = { obj, NULL, NULL, NULL };
DWORD wait_count = 1;
DWORD timeout;
HANDLE proc = NULL;
if (lock->lf_flags & F_POSIX)
{
proc = OpenProcess (SYNCHRONIZE, FALSE, block->lf_wid);
if (!proc)
timeout = 0L;
else
{
w4[wait_count++] = proc;
timeout = INFINITE;
}
}
else
timeout = 100L;
DWORD WAIT_SIGNAL_ARRIVED = WAIT_OBJECT_0 + wait_count;
wait_signal_arrived here (w4[wait_count++]);
DWORD WAIT_THREAD_CANCELED = WAIT_TIMEOUT + 1;
HANDLE cancel_event = pthread::get_cancel_event ();
if (cancel_event)
{
WAIT_THREAD_CANCELED = WAIT_OBJECT_0 + wait_count;
w4[wait_count++] = cancel_event;
}
/* Wait for the blocking object and, for POSIX locks, its holding process.
Unfortunately, since BSD flock locks are not attached to a specific
process, we can't recognize an abandoned lock by sync'ing with the
creator process. We have to make sure the event object is in a
signalled state, or that it has gone away. The latter we can only
recognize by retrying to fetch the block list, so we must not wait
infinitely. For POSIX locks, if the process has already exited,
just check if a signal or a thread cancel request arrived. */
SetThreadPriority (GetCurrentThread (), priority);
DWORD ret = WaitForMultipleObjects (wait_count, w4, FALSE, timeout);
SetThreadPriority (GetCurrentThread (), old_prio);
if (proc)
CloseHandle (proc);
node->LOCK ();
/* Never close lock object handle outside of node lock! */
NtClose (obj);
if (ret == WAIT_SIGNAL_ARRIVED)
{
/* A signal came in. */
lock->lf_next = *clean;
*clean = lock;
return EINTR;
}
else if (ret == WAIT_THREAD_CANCELED)
{
/* The thread has been sent a cancellation request. */
lock->lf_next = *clean;
*clean = lock;
return ECANCELED;
}
else
/* The lock object has been set to signalled or ...
for POSIX locks, the process holding the lock has exited, or ...
just a timeout. Just retry. */
continue;
}
allow_others_to_sync ();
/*
* No blocks!! Add the lock. Note that we will
* downgrade or upgrade any overlapping locks this
* process already owns.
*
* Handle any locks that overlap.
*/
prev = head;
block = *head;
needtolink = 1;
for (;;)
{
ovcase = lf_findoverlap (block, lock, SELF, &prev, &overlap);
if (ovcase)
block = overlap->lf_next;
/*
* Six cases:
* 0) no overlap
* 1) overlap == lock
* 2) overlap contains lock
* 3) lock contains overlap
* 4) overlap starts before lock
* 5) overlap ends after lock
*/
switch (ovcase)
{
case 0: /* no overlap */
if (needtolink)
{
*prev = lock;
lock->lf_next = overlap;
lock->create_lock_obj ();
}
break;
case 1: /* overlap == lock */
/*
* If downgrading lock, others may be
* able to acquire it.
* Cygwin: Always wake lock.
*/
lf_wakelock (overlap, fhdl);
overlap->lf_type = lock->lf_type;
overlap->create_lock_obj ();
lock->lf_next = *clean;
*clean = lock;
break;
case 2: /* overlap contains lock */
/*
* Check for common starting point and different types.
*/
if (overlap->lf_type == lock->lf_type)
{
lock->lf_next = *clean;
*clean = lock;
break;
}
if (overlap->lf_start == lock->lf_start)
{
*prev = lock;
lock->lf_next = overlap;
overlap->lf_start = lock->lf_end + 1;
}
else
lf_split (overlap, lock, clean);
lf_wakelock (overlap, fhdl);
overlap->create_lock_obj ();
lock->create_lock_obj ();
if (lock->lf_next && !lock->lf_next->lf_obj)
lock->lf_next->create_lock_obj ();
break;
case 3: /* lock contains overlap */
/*
* If downgrading lock, others may be able to
* acquire it, otherwise take the list.
* Cygwin: Always wake old lock and create new lock.
*/
lf_wakelock (overlap, fhdl);
/*
* Add the new lock if necessary and delete the overlap.
*/
if (needtolink)
{
*prev = lock;
lock->lf_next = overlap->lf_next;
prev = &lock->lf_next;
lock->create_lock_obj ();
needtolink = 0;
}
else
*prev = overlap->lf_next;
overlap->lf_next = *clean;
*clean = overlap;
continue;
case 4: /* overlap starts before lock */
/*
* Add lock after overlap on the list.
*/
lock->lf_next = overlap->lf_next;
overlap->lf_next = lock;
overlap->lf_end = lock->lf_start - 1;
prev = &lock->lf_next;
lf_wakelock (overlap, fhdl);
overlap->create_lock_obj ();
lock->create_lock_obj ();
needtolink = 0;
continue;
case 5: /* overlap ends after lock */
/*
* Add the new lock before overlap.
*/
if (needtolink) {
*prev = lock;
lock->lf_next = overlap;
}
overlap->lf_start = lock->lf_end + 1;
lf_wakelock (overlap, fhdl);
lock->create_lock_obj ();
overlap->create_lock_obj ();
break;
}
break;
}
return 0;
}
/*
* Remove a byte-range lock on an inode_t.
*
* Generally, find the lock (or an overlap to that lock)
* and remove it (or shrink it), then wakeup anyone we can.
*/
static int
lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE fhdl)
{
lockf_t **head = unlock->lf_head;
lockf_t *lf = *head;
lockf_t *overlap, **prev;
int ovcase;
if (lf == NOLOCKF)
return 0;
prev = head;
while ((ovcase = lf_findoverlap (lf, unlock, SELF, &prev, &overlap)))
{
/*
* Wakeup the list of locks to be retried.
*/
lf_wakelock (overlap, fhdl);
switch (ovcase)
{
case 1: /* overlap == lock */
*prev = overlap->lf_next;
overlap->lf_next = *clean;
*clean = overlap;
break;
case 2: /* overlap contains lock: split it */
if (overlap->lf_start == unlock->lf_start)
{
overlap->lf_start = unlock->lf_end + 1;
overlap->create_lock_obj ();
break;
}
lf_split (overlap, unlock, clean);
overlap->lf_next = unlock->lf_next;
overlap->create_lock_obj ();
if (overlap->lf_next && !overlap->lf_next->lf_obj)
overlap->lf_next->create_lock_obj ();
break;
case 3: /* lock contains overlap */
*prev = overlap->lf_next;
lf = overlap->lf_next;
overlap->lf_next = *clean;
*clean = overlap;
continue;
case 4: /* overlap starts before lock */
overlap->lf_end = unlock->lf_start - 1;
prev = &overlap->lf_next;
lf = overlap->lf_next;
overlap->create_lock_obj ();
continue;
case 5: /* overlap ends after lock */
overlap->lf_start = unlock->lf_end + 1;
overlap->create_lock_obj ();
break;
}
break;
}
return 0;
}
/*
* Check whether there is a blocking lock,
* and if so return its process identifier.
*/
static int
lf_getlock (lockf_t *lock, inode_t *node, struct flock *fl)
{
lockf_t *block;
tmp_pathbuf tp;
/* Create temporary space for the all locks list. */
node->i_all_lf = (lockf_t *) (void * ) tp.w_get ();
if ((block = lf_getblock (lock, node)))
{
if (block->lf_obj)
block->close_lock_obj ();
fl->l_type = block->lf_type;
fl->l_whence = SEEK_SET;
fl->l_start = block->lf_start;
if (block->lf_end == -1)
fl->l_len = 0;
else
fl->l_len = block->lf_end - block->lf_start + 1;
if (block->lf_flags & F_POSIX)
fl->l_pid = (pid_t) block->lf_id;
else
fl->l_pid = -1;
}
else
fl->l_type = F_UNLCK;
return 0;
}
/*
* Walk the list of locks for an inode_t and
* return the first blocking lock.
*/
static lockf_t *
lf_getblock (lockf_t *lock, inode_t *node)
{
lockf_t **prev, *overlap;
lockf_t *lf = node->get_all_locks_list ();
int ovcase;
prev = lock->lf_head;
while ((ovcase = lf_findoverlap (lf, lock, OTHERS, &prev, &overlap)))
{
/*
* We've found an overlap, see if it blocks us
*/
if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK))
{
/* Open the event object for synchronization. */
if (overlap->open_lock_obj ())
{
/* Check if the event object is signalled. If so, the overlap
doesn't actually exist anymore. There are just a few open
handles left. */
if (!IsEventSignalled (overlap->lf_obj))
return overlap;
overlap->close_lock_obj ();
}
}
/*
* Nope, point to the next one on the list and
* see if it blocks us
*/
lf = overlap->lf_next;
}
return NOLOCKF;
}
/*
* Walk the list of locks for an inode_t to
* find an overlapping lock (if any).
*
* NOTE: this returns only the FIRST overlapping lock. There
* may be more than one.
*/
static int
lf_findoverlap (lockf_t *lf, lockf_t *lock, int type, lockf_t ***prev,
lockf_t **overlap)
{
off_t start, end;
*overlap = lf;
if (lf == NOLOCKF)
return 0;
start = lock->lf_start;
end = lock->lf_end;
while (lf != NOLOCKF)
{
if (((type & SELF) && lf->lf_id != lock->lf_id)
|| ((type & OTHERS) && lf->lf_id == lock->lf_id)
/* As on Linux: POSIX locks and BSD flock locks don't interact. */
|| (lf->lf_flags & (F_POSIX | F_FLOCK))
!= (lock->lf_flags & (F_POSIX | F_FLOCK)))
{
*prev = &lf->lf_next;
*overlap = lf = lf->lf_next;
continue;
}
/*
* OK, check for overlap
*
* Six cases:
* 0) no overlap
* 1) overlap == lock
* 2) overlap contains lock
* 3) lock contains overlap
* 4) overlap starts before lock
* 5) overlap ends after lock
*/
if ((lf->lf_end != -1 && start > lf->lf_end) ||
(end != -1 && lf->lf_start > end))
{
/* Case 0 */
if ((type & SELF) && end != -1 && lf->lf_start > end)
return 0;
*prev = &lf->lf_next;
*overlap = lf = lf->lf_next;
continue;
}
if ((lf->lf_start == start) && (lf->lf_end == end))
{
/* Case 1 */
return 1;
}
if ((lf->lf_start <= start) && (end != -1) &&
((lf->lf_end >= end) || (lf->lf_end == -1)))
{
/* Case 2 */
return 2;
}
if (start <= lf->lf_start && (end == -1 ||
(lf->lf_end != -1 && end >= lf->lf_end)))
{
/* Case 3 */
return 3;
}
if ((lf->lf_start < start) &&
((lf->lf_end >= start) || (lf->lf_end == -1)))
{
/* Case 4 */
return 4;
}
if ((lf->lf_start > start) && (end != -1) &&
((lf->lf_end > end) || (lf->lf_end == -1)))
{
/* Case 5 */
return 5;
}
api_fatal ("lf_findoverlap: default\n");
}
return 0;
}
/*
* Split a lock and a contained region into
* two or three locks as necessary.
*/
static void
lf_split (lockf_t *lock1, lockf_t *lock2, lockf_t **split)
{
lockf_t *splitlock;
/*
* Check to see if spliting into only two pieces.
*/
if (lock1->lf_start == lock2->lf_start)
{
lock1->lf_start = lock2->lf_end + 1;
lock2->lf_next = lock1;
return;
}
if (lock1->lf_end == lock2->lf_end)
{
lock1->lf_end = lock2->lf_start - 1;
lock2->lf_next = lock1->lf_next;
lock1->lf_next = lock2;
return;
}
/*
* Make a new lock consisting of the last part of
* the encompassing lock. We use the preallocated
* splitlock so we don't have to block.
*/
splitlock = *split;
assert (splitlock != NULL);
*split = splitlock->lf_next;
memcpy (splitlock, lock1, sizeof *splitlock);
/* We have to unset the obj HANDLE here which has been copied by the
above memcpy, so that the calling function recognizes the new object.
See post-lf_split handling in lf_setlock and lf_clearlock. */
splitlock->lf_obj = NULL;
splitlock->lf_start = lock2->lf_end + 1;
lock1->lf_end = lock2->lf_start - 1;
/*
* OK, now link it in
*/
splitlock->lf_next = lock1->lf_next;
lock2->lf_next = splitlock;
lock1->lf_next = lock2;
}
/*
* Wakeup a blocklist
* Cygwin: Just signal the lock which gets removed. This unblocks
* all threads waiting for this lock.
*/
static void
lf_wakelock (lockf_t *listhead, HANDLE fhdl)
{
listhead->del_lock_obj (fhdl, true);
}
extern "C" int
flock (int fd, int operation)
{
int res = -1;
int cmd;
struct flock fl = { 0, SEEK_SET, 0, 0, 0 };
__try
{
cygheap_fdget cfd (fd);
if (cfd < 0)
__leave;
cmd = (operation & LOCK_NB) ? F_SETLK : F_SETLKW;
switch (operation & (~LOCK_NB))
{
case LOCK_EX:
fl.l_type = F_WRLCK;
break;
case LOCK_SH:
fl.l_type = F_RDLCK;
break;
case LOCK_UN:
fl.l_type = F_UNLCK;
break;
default:
set_errno (EINVAL);
__leave;
}
if (!cfd->mandatory_locking ())
fl.l_type |= F_FLOCK;
res = cfd->mandatory_locking () ? cfd->mand_lock (cmd, &fl)
: cfd->lock (cmd, &fl);
if ((res == -1) && ((get_errno () == EAGAIN) || (get_errno () == EACCES)))
set_errno (EWOULDBLOCK);
}
__except (EFAULT) {}
__endtry
syscall_printf ("%R = flock(%d, %d)", res, fd, operation);
return res;
}
extern "C" int
lockf (int filedes, int function, off_t size)
{
int res = -1;
int cmd;
struct flock fl;
pthread_testcancel ();
__try
{
cygheap_fdget cfd (filedes);
if (cfd < 0)
__leave;
fl.l_start = 0;
fl.l_len = size;
fl.l_whence = SEEK_CUR;
switch (function)
{
case F_ULOCK:
cmd = F_SETLK;
fl.l_type = F_UNLCK;
break;
case F_LOCK:
cmd = F_SETLKW;
fl.l_type = F_WRLCK;
break;
case F_TLOCK:
cmd = F_SETLK;
fl.l_type = F_WRLCK;
break;
case F_TEST:
fl.l_type = F_WRLCK;
if (cfd->lock (F_GETLK, &fl) == -1)
__leave;
if (fl.l_type == F_UNLCK || fl.l_pid == getpid ())
res = 0;
else
errno = EAGAIN;
__leave;
/* NOTREACHED */
default:
errno = EINVAL;
__leave;
/* NOTREACHED */
}
res = cfd->mandatory_locking () ? cfd->mand_lock (cmd, &fl)
: cfd->lock (cmd, &fl);
}
__except (EFAULT) {}
__endtry
syscall_printf ("%R = lockf(%d, %d, %D)", res, filedes, function, size);
return res;
}
/* This is the fcntl lock implementation for mandatory locks using the
Windows mandatory locking functions. For the UNIX-like advisory locking
implementation see the fhandler_disk_file::lock method earlier in this
file. */
struct lock_parms {
HANDLE h;
PIO_STATUS_BLOCK pio;
PLARGE_INTEGER poff;
PLARGE_INTEGER plen;
BOOL type;
NTSTATUS status;
};
static DWORD WINAPI
blocking_lock_thr (LPVOID param)
{
struct lock_parms *lp = (struct lock_parms *) param;
lp->status = NtLockFile (lp->h, NULL, NULL, NULL, lp->pio, lp->poff,
lp->plen, 0, FALSE, lp->type);
return 0;
}
int
fhandler_base::mand_lock (int, struct flock *)
{
set_errno (EINVAL);
return -1;
}
int
fhandler_disk_file::mand_lock (int a_op, struct flock *fl)
{
NTSTATUS status;
IO_STATUS_BLOCK io;
FILE_POSITION_INFORMATION fpi;
FILE_STANDARD_INFORMATION fsi;
off_t startpos;
LARGE_INTEGER offset;
LARGE_INTEGER length;
/* Calculate where to start from, then adjust this by fl->l_start. */
switch (fl->l_whence)
{
case SEEK_SET:
startpos = 0;
break;
case SEEK_CUR:
status = NtQueryInformationFile (get_handle (), &io, &fpi, sizeof fpi,
FilePositionInformation);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
startpos = fpi.CurrentByteOffset.QuadPart;
break;
case SEEK_END:
status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
FileStandardInformation);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
startpos = fsi.EndOfFile.QuadPart;
break;
default:
set_errno (EINVAL);
return -1;
}
/* Adjust start and length until they make sense. */
offset.QuadPart = startpos + fl->l_start;
if (fl->l_len < 0)
{
offset.QuadPart -= fl->l_len;
length.QuadPart = -fl->l_len;
}
else
length.QuadPart = fl->l_len;
if (offset.QuadPart < 0)
{
length.QuadPart -= -offset.QuadPart;
if (length.QuadPart <= 0)
{
set_errno (EINVAL);
return -1;
}
offset.QuadPart = 0;
}
/* Special case if len == 0. For POSIX this means lock to the end of
the entire file, even when file grows later. */
if (length.QuadPart == 0)
length.QuadPart = UINT64_MAX;
/* Action! */
if (fl->l_type == F_UNLCK)
{
status = NtUnlockFile (get_handle (), &io, &offset, &length, 0);
if (status == STATUS_RANGE_NOT_LOCKED) /* Not an error */
status = STATUS_SUCCESS;
}
else if (a_op == F_SETLKW)
{
/* We open file handles synchronously. To allow asynchronous operation
the file locking functions require a file handle opened in asynchronous
mode. Since Windows locks are per-process/per-file object, we can't
open another handle asynchrously and lock/unlock using that handle:
The original file handle would not have placed the lock and would be
restricted by the lock like any other file handle.
So, what we do here is to start a thread which calls the potentially
blocking NtLockFile call. Then we wait for thread completion in an
interruptible fashion. */
OBJECT_ATTRIBUTES attr;
HANDLE evt;
struct lock_parms lp = { get_handle (), &io, &offset, &length,
fl->l_type == F_WRLCK, 0 };
cygthread *thr = NULL;
InitializeObjectAttributes (&attr, NULL, 0, NULL, NULL);
status = NtCreateEvent (&evt, EVENT_ALL_ACCESS, &attr,
NotificationEvent, FALSE);
if (evt)
thr = new cygthread (blocking_lock_thr, &lp, "blk_lock", evt);
if (!thr)
{
/* Thread creation failed. Fall back to blocking lock call. */
if (evt)
NtClose (evt);
status = NtLockFile (get_handle (), NULL, NULL, NULL, &io, &offset,
&length, 0, FALSE, fl->l_type == F_WRLCK);
}
else
{
/* F_SETLKW and lock cannot be established. Wait until the lock can
be established, or a signal request arrived. We deliberately
don't handle thread cancel requests here. */
DWORD wait_res = cygwait (evt, INFINITE, cw_sig | cw_sig_eintr);
NtClose (evt);
switch (wait_res)
{
case WAIT_OBJECT_0:
/* Fetch completion status. */
status = lp.status;
thr->detach ();
break;
default:
/* Signal arrived.
If CancelSynchronousIo works we wait for the thread to exit.
lp.status will be either STATUS_SUCCESS, or STATUS_CANCELLED.
We only call NtUnlockFile in the first case.
If CancelSynchronousIo fails we terminated the thread and
call NtUnlockFile since lp.status was 0 to begin with. */
if (CancelSynchronousIo (thr->thread_handle ()))
thr->detach ();
else
thr->terminate_thread ();
if (NT_SUCCESS (lp.status))
NtUnlockFile (get_handle (), &io, &offset, &length, 0);
/* Per SUSv4: If a signal is received while fcntl is waiting,
fcntl shall be interrupted. Upon return from the signal
handler, fcntl shall return -1 with errno set to EINTR,
and the lock operation shall not be done. */
_my_tls.call_signal_handler ();
set_errno (EINTR);
return -1;
}
}
}
else
{
status = NtLockFile (get_handle (), NULL, NULL, NULL, &io, &offset,
&length, 0, TRUE, fl->l_type == F_WRLCK);
if (a_op == F_GETLK)
{
/* This is non-atomic, but there's no other way on Windows to detect
if another lock is blocking our lock, other than trying to place
the lock, and then having to unlock it again. */
if (NT_SUCCESS (status))
{
NtUnlockFile (get_handle (), &io, &offset, &length, 0);
fl->l_type = F_UNLCK;
}
else
{
/* FAKE! FAKE! FAKE! */
fl->l_type = F_WRLCK;
fl->l_whence = SEEK_SET;
fl->l_start = offset.QuadPart;
fl->l_len = length.QuadPart;
fl->l_pid = (pid_t) -1;
}
status = STATUS_SUCCESS;
}
}
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
return 0;
}
|