1/* 2 * Copyright (c) 2002-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */ 24/* $FreeBSD: src/usr.sbin/rpc.lockd/lockd_lock.c,v 1.10 2002/03/22 19:57:09 alfred Exp $ */ 25 26/* 27 * Copyright (c) 2001 Andrew P. Lentvorski, Jr. 28 * Copyright (c) 2000 Manuel Bouyer. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in the 37 * documentation and/or other materials provided with the distribution. 38 * 3. All advertising materials mentioning features or use of this software 39 * must display the following acknowledgement: 40 * This product includes software developed by the University of 41 * California, Berkeley and its contributors. 42 * 4. Neither the name of the University nor the names of its contributors 43 * may be used to endorse or promote products derived from this software 44 * without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 * 58 */ 59 60#define LOCKD_DEBUG 61 62#include <stdio.h> 63#ifdef LOCKD_DEBUG 64#include <stdarg.h> 65#endif 66#include <stdlib.h> 67#include <unistd.h> 68#include <fcntl.h> 69#include <syslog.h> 70#include <errno.h> 71#include <string.h> 72#include <signal.h> 73#include <oncrpc/rpc.h> 74#include <sys/types.h> 75#include <sys/stat.h> 76#include <sys/socket.h> 77#include <sys/param.h> 78#include <sys/mount.h> 79#include <sys/wait.h> 80#include <arpa/inet.h> 81#include "sm_inter.h" 82#include "nlm_prot.h" 83 84#include <nfs/rpcv2.h> 85#include <nfs/nfs_lock.h> 86#include <nfs/nfs.h> 87 88#include "lockd.h" 89#include "lockd_lock.h" 90 91#define MAXOBJECTSIZE 64 92#define MAXBUFFERSIZE 1024 93 94#define AOK (void *) // assert alignment is OK 95 96/* 97 * A set of utilities for managing file locking 98 * 99 * XXX: All locks are in a linked list, a better structure should be used 100 * to improve search/access effeciency. 101 */ 102 103/* struct describing a lock */ 104struct file_lock { 105 LIST_ENTRY(file_lock) nfslocklist; 106 netobj filehandle; /* NFS filehandle */ 107 struct sockaddr *addr; 108 struct nlm4_holder client; /* lock holder */ 109 u_int64_t granted_cookie; 110 int nsm_status; /* status from the remote lock manager */ 111 int status; /* lock status, see below */ 112 int flags; /* lock flags, see lockd_lock.h */ 113 int blocking; /* blocking lock or not */ 114 char client_name[SM_MAXSTRLEN]; /* client_name is really variable length and must be last! */ 115}; 116 117LIST_HEAD(nfslocklist_head, file_lock); 118struct nfslocklist_head nfslocklist_head = LIST_HEAD_INITIALIZER(nfslocklist_head); 119 120LIST_HEAD(blockedlocklist_head, file_lock); 121struct blockedlocklist_head blockedlocklist_head = LIST_HEAD_INITIALIZER(blockedlocklist_head); 122 123/* struct describing a share reservation */ 124struct file_share { 125 LIST_ENTRY(file_share) nfssharelist; 126 netobj oh; /* share holder */ 127 short mode; 128 short access; 129 char client_name[SM_MAXSTRLEN]; /* name is really variable length and must be last! */ 130}; 131LIST_HEAD(nfssharelist_head, file_share); 132 133/* Struct describing a file with share reservations */ 134struct sharefile { 135 LIST_ENTRY(sharefile) sharefilelist; 136 netobj filehandle; /* Local access filehandle */ 137 int fd; /* file descriptor: remains open until no more shares */ 138 int refcount; 139 struct nfssharelist_head sharelist_head; 140}; 141LIST_HEAD(nfssharefilelist_head, sharefile); 142struct nfssharefilelist_head nfssharefilelist_head = LIST_HEAD_INITIALIZER(nfssharefilelist_head); 143 144/* lock status */ 145#define LKST_LOCKED 1 /* lock is locked */ 146/* XXX: Is this flag file specific or lock specific? */ 147#define LKST_WAITING 2 /* file is already locked by another host */ 148#define LKST_PROCESSING 3 /* child is trying to aquire the lock */ 149#define LKST_DYING 4 /* must dies when we get news from the child */ 150 151/* struct describing a monitored host */ 152struct host { 153 TAILQ_ENTRY(host) hostlst; 154 int refcnt; 155 time_t lastuse; 156 struct sockaddr_storage addr; 157 char *name; /* host name provided by client via caller_name */ 158 char *revname; /* host name mapped from addr */ 159}; 160/* list of hosts we monitor */ 161TAILQ_HEAD(hostlst_head, host); 162struct hostlst_head hostlst_head = TAILQ_HEAD_INITIALIZER(hostlst_head); 163struct hostlst_head hostlst_unref = TAILQ_HEAD_INITIALIZER(hostlst_unref); 164 165u_int64_t send_granted_cookie = 0; 166 167/* 168 * File monitoring handlers 169 * XXX: These might be able to be removed when kevent support 170 * is placed into the hardware lock/unlock routines. (ie. 171 * let the kernel do all the file monitoring) 172 */ 173 174/* Struct describing a monitored file */ 175struct monfile { 176 LIST_ENTRY(monfile) monfilelist; 177 netobj filehandle; /* Local access filehandle */ 178 int fd; /* file descriptor: remains open until unlock! */ 179 int refcount; 180 int exclusive; 181}; 182 183/* List of files we monitor */ 184LIST_HEAD(monfilelist_head, monfile); 185struct monfilelist_head monfilelist_head = LIST_HEAD_INITIALIZER(monfilelist_head); 186 187static int debugdelay = 0; 188 189enum nfslock_status { NFS_GRANTED = 0, NFS_GRANTED_DUPLICATE, 190 NFS_DENIED, NFS_DENIED_NOLOCK, 191 NFS_RESERR }; 192 193enum hwlock_status { HW_GRANTED = 0, HW_GRANTED_DUPLICATE, 194 HW_DENIED, HW_DENIED_NOLOCK, 195 HW_STALEFH, HW_READONLY, HW_RESERR }; 196 197enum partialfilelock_status { PFL_GRANTED=0, PFL_GRANTED_DUPLICATE, PFL_DENIED, 198 PFL_NFSDENIED, PFL_NFSBLOCKED, PFL_NFSDENIED_NOLOCK, PFL_NFSRESERR, 199 PFL_HWDENIED, PFL_HWBLOCKED, PFL_HWDENIED_NOLOCK, PFL_HWRESERR, 200 PFL_HWDENIED_STALEFH, PFL_HWDENIED_READONLY }; 201 202enum LFLAGS {LEDGE_LEFT, LEDGE_LBOUNDARY, LEDGE_INSIDE, LEDGE_RBOUNDARY, LEDGE_RIGHT}; 203enum RFLAGS {REDGE_LEFT, REDGE_LBOUNDARY, REDGE_INSIDE, REDGE_RBOUNDARY, REDGE_RIGHT}; 204/* XXX: WARNING! I HAVE OVERLOADED THIS STATUS ENUM! SPLIT IT APART INTO TWO */ 205enum split_status {SPL_DISJOINT=0, SPL_LOCK1=1, SPL_LOCK2=2, SPL_CONTAINED=4, SPL_RESERR=8}; 206 207enum partialfilelock_status lock_partialfilelock(struct file_lock *fl); 208 209int send_granted(struct file_lock *fl, int opcode); 210void siglock(void); 211void sigunlock(void); 212void destroy_lock_host(struct host *ihp); 213static void monitor_lock_host(const char *hostname, const struct sockaddr *addr); 214 215void copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src, 216 const bool_t exclusive, struct nlm4_holder *dest); 217struct file_lock * allocate_file_lock(const netobj *lockowner, 218 const netobj *filehandle, const struct sockaddr *addr, 219 const char *caller_name); 220void deallocate_file_lock(struct file_lock *fl); 221void fill_file_lock(struct file_lock *fl, 222 const bool_t exclusive, const int32_t svid, 223 const u_int64_t offset, const u_int64_t len, 224 const int state, const int status, const int flags, const int blocking); 225int regions_overlap(const u_int64_t start1, const u_int64_t len1, 226 const u_int64_t start2, const u_int64_t len2);; 227enum split_status region_compare(const u_int64_t starte, const u_int64_t lene, 228 const u_int64_t startu, const u_int64_t lenu, 229 u_int64_t *start1, u_int64_t *len1, u_int64_t *start2, u_int64_t *len2); 230int same_netobj(const netobj *n0, const netobj *n1); 231int same_filelock_identity(const struct file_lock *fl0, 232 const struct file_lock *fl2); 233 234static void debuglog(char const *fmt, ...); 235void dump_static_object(const unsigned char* object, const int sizeof_object, 236 unsigned char* hbuff, const int sizeof_hbuff, 237 unsigned char* cbuff, const int sizeof_cbuff); 238void dump_netobj(const struct netobj *nobj); 239void dump_filelock(const struct file_lock *fl); 240struct file_lock * get_lock_matching_unlock(const struct file_lock *fl, int cleanup); 241enum nfslock_status test_nfslock(const struct file_lock *fl, 242 struct file_lock **conflicting_fl); 243enum nfslock_status lock_nfslock(struct file_lock *fl); 244enum nfslock_status delete_nfslock(struct file_lock *fl); 245enum nfslock_status unlock_nfslock(const struct file_lock *fl, 246 struct file_lock **released_lock, struct file_lock **left_lock, 247 struct file_lock **right_lock, int cleanup); 248enum hwlock_status lock_hwlock(struct file_lock *fl); 249enum split_status split_nfslock(const struct file_lock *exist_lock, 250 const struct file_lock *unlock_lock, struct file_lock **left_lock, 251 struct file_lock **right_lock); 252void add_blockingfilelock(struct file_lock *fl); 253enum hwlock_status unlock_hwlock(const struct file_lock *fl); 254enum hwlock_status test_hwlock(const struct file_lock *fl, 255 struct file_lock **conflicting_fl); 256void remove_blockingfilelock(struct file_lock *fl); 257void clear_blockingfilelock(const char *hostname); 258void retry_blockingfilelocklist(netobj *fh); 259enum partialfilelock_status unlock_partialfilelock( 260 const struct file_lock *fl, int cleanup); 261void clear_partialfilelock(const char *hostname); 262enum partialfilelock_status test_partialfilelock( 263 const struct file_lock *fl, struct file_lock **conflicting_fl); 264enum nlm4_stats do_test(struct file_lock *fl, struct file_lock **conflicting_fl); 265enum nlm4_stats do_unlock(struct file_lock *fl); 266enum nlm4_stats do_lock(struct file_lock *fl); 267void do_clear(const char *hostname); 268 269 270#pragma clang diagnostic push 271#pragma clang diagnostic ignored "-Wformat-nonliteral" 272 273void 274debuglog(char const *fmt, ...) 275{ 276 va_list ap; 277 278 if (config.verbose < 1) { 279 return; 280 } 281 282 sleep(debugdelay); 283 284 va_start(ap, fmt); 285 vsyslog(LOG_DEBUG, fmt, ap); 286 va_end(ap); 287} 288#pragma clang diagnostic pop 289 290void 291dump_static_object(object, size_object, hbuff, size_hbuff, cbuff, size_cbuff) 292 const unsigned char *object; 293 const int size_object; 294 unsigned char *hbuff; 295 const int size_hbuff; 296 unsigned char *cbuff; 297 const int size_cbuff; 298{ 299 int i, objectsize; 300 char *tmp = (char*)hbuff; 301 302 if (config.verbose < 2) { 303 return; 304 } 305 306 objectsize = size_object; 307 308 if (objectsize == 0) { 309 debuglog("object is size 0\n"); 310 } else { 311 if (objectsize > MAXOBJECTSIZE) { 312 debuglog("Object of size %d being clamped" 313 "to size %d\n", objectsize, MAXOBJECTSIZE); 314 objectsize = MAXOBJECTSIZE; 315 } 316 317 if (hbuff != NULL) { 318 if (size_hbuff < objectsize*2+1) { 319 debuglog("Hbuff not large enough." 320 " Increase size\n"); 321 } else { 322 for(i=0;i<objectsize;i++,tmp+=2) { 323 snprintf(tmp, size_hbuff - (2*i), "%02X", *(object+i)); 324 } 325 *tmp = '\0'; 326 } 327 } 328 329 if (cbuff != NULL) { 330 if (size_cbuff < objectsize+1) { 331 debuglog("Cbuff not large enough." 332 " Increase Size\n"); 333 } 334 335 for(i=0;i<objectsize;i++) { 336 if (*(object+i) >= 32 && *(object+i) <= 127) { 337 *(cbuff+i) = *(object+i); 338 } else { 339 *(cbuff+i) = '.'; 340 } 341 } 342 *(cbuff+i) = '\0'; 343 } 344 } 345} 346 347void 348dump_netobj(const struct netobj *nobj) 349{ 350 char hbuff[MAXBUFFERSIZE*2]; 351 char cbuff[MAXBUFFERSIZE]; 352 353 if (config.verbose < 2) { 354 return; 355 } 356 357 if (nobj == NULL) { 358 debuglog("Null netobj pointer\n"); 359 } 360 else if (nobj->n_len == 0) { 361 debuglog("Size zero netobj\n"); 362 } else { 363 dump_static_object((const unsigned char *)nobj->n_bytes, nobj->n_len, 364 (unsigned char *)hbuff, sizeof(hbuff), (unsigned char *)cbuff, sizeof(cbuff)); 365 debuglog("netobj: len: %d data: %s ::: %s\n", 366 nobj->n_len, hbuff, cbuff); 367 } 368} 369 370/* #define DUMP_FILELOCK_VERBOSE */ 371void 372dump_filelock(const struct file_lock *fl) 373{ 374#ifdef DUMP_FILELOCK_VERBOSE 375 char hbuff[MAXBUFFERSIZE*2]; 376 char cbuff[MAXBUFFERSIZE]; 377#endif 378 379 if (config.verbose < 2) { 380 return; 381 } 382 383 if (fl != NULL) { 384 debuglog("Dumping file lock structure @ %p\n", fl); 385 386#ifdef DUMP_FILELOCK_VERBOSE 387 dump_static_object((unsigned char *)&fl->filehandle.n_bytes, 388 fl->filehandle.n_len, hbuff, sizeof(hbuff), 389 cbuff, sizeof(cbuff)); 390 debuglog("Filehandle: %8s ::: %8s\n", hbuff, cbuff); 391#endif 392 393 debuglog("Dumping nlm4_holder:\n" 394 "exc: %x svid: %x offset:len %llx:%llx\n", 395 fl->client.exclusive, fl->client.svid, 396 fl->client.l_offset, fl->client.l_len); 397 398#ifdef DUMP_FILELOCK_VERBOSE 399 debuglog("Dumping client identity:\n"); 400 dump_netobj(&fl->client.oh); 401 402 debuglog("nsm: %d status: %d flags: %d locker: %d" 403 " fd: %d\n", fl->nsm_status, fl->status, 404 fl->flags, fl->locker, fl->fd); 405#endif 406 } else { 407 debuglog("NULL file lock structure\n"); 408 } 409} 410 411void 412copy_nlm4_lock_to_nlm4_holder(src, exclusive, dest) 413 const struct nlm4_lock *src; 414 const bool_t exclusive; 415 struct nlm4_holder *dest; 416{ 417 418 dest->exclusive = exclusive; 419 dest->oh.n_len = src->oh.n_len; 420 dest->oh.n_bytes = src->oh.n_bytes; 421 dest->svid = src->svid; 422 dest->l_offset = src->l_offset; 423 dest->l_len = src->l_len; 424} 425 426 427/* 428 * allocate_file_lock: Create a lock with the given parameters 429 */ 430 431struct file_lock * 432allocate_file_lock(const netobj *lockowner, const netobj *filehandle, 433 const struct sockaddr *addr, const char *caller_name) 434{ 435 struct file_lock *newfl; 436 size_t n; 437 438 /* Beware of rubbish input! */ 439 n = strnlen(caller_name, SM_MAXSTRLEN); 440 if (n == SM_MAXSTRLEN) { 441 return NULL; 442 } 443 444 newfl = malloc(sizeof(*newfl) - sizeof(newfl->client_name) + n + 1); 445 if (newfl == NULL) { 446 return NULL; 447 } 448 bzero(newfl, sizeof(*newfl) - sizeof(newfl->client_name)); 449 memcpy(newfl->client_name, caller_name, n); 450 newfl->client_name[n] = 0; 451 452 newfl->client.oh.n_bytes = malloc(lockowner->n_len); 453 if (newfl->client.oh.n_bytes == NULL) { 454 free(newfl); 455 return NULL; 456 } 457 newfl->client.oh.n_len = lockowner->n_len; 458 bcopy(lockowner->n_bytes, newfl->client.oh.n_bytes, lockowner->n_len); 459 460 newfl->filehandle.n_bytes = malloc(filehandle->n_len); 461 if (newfl->filehandle.n_bytes == NULL) { 462 free(newfl->client.oh.n_bytes); 463 free(newfl); 464 return NULL; 465 } 466 newfl->filehandle.n_len = filehandle->n_len; 467 bcopy(filehandle->n_bytes, newfl->filehandle.n_bytes, filehandle->n_len); 468 469 newfl->addr = malloc(addr->sa_len); 470 if (newfl->addr == NULL) { 471 free(newfl->client.oh.n_bytes); 472 free(newfl); 473 return NULL; 474 } 475 memcpy(newfl->addr, addr, addr->sa_len); 476 477 return newfl; 478} 479 480/* 481 * file_file_lock: Force creation of a valid file lock 482 */ 483void 484fill_file_lock(struct file_lock *fl, 485 const bool_t exclusive, const int32_t svid, 486 const u_int64_t offset, const u_int64_t len, 487 const int state, const int status, const int flags, const int blocking) 488{ 489 fl->client.exclusive = exclusive; 490 fl->client.svid = svid; 491 fl->client.l_offset = offset; 492 fl->client.l_len = len; 493 494 fl->nsm_status = state; 495 fl->status = status; 496 fl->flags = flags; 497 fl->blocking = blocking; 498} 499 500/* 501 * deallocate_file_lock: Free all storage associated with a file lock 502 */ 503void 504deallocate_file_lock(struct file_lock *fl) 505{ 506 free(fl->addr); 507 free(fl->client.oh.n_bytes); 508 free(fl->filehandle.n_bytes); 509 free(fl); 510} 511 512/* 513 * regions_overlap(): This function examines the two provided regions for 514 * overlap. 515 */ 516int 517regions_overlap(start1, len1, start2, len2) 518 const u_int64_t start1, len1, start2, len2; 519{ 520 u_int64_t d1,d2,d3,d4; 521 enum split_status result; 522 523 debuglog("Entering region overlap with vals: %llu:%llu--%llu:%llu\n", 524 start1, len1, start2, len2); 525 526 result = region_compare(start1, len1, start2, len2, 527 &d1, &d2, &d3, &d4); 528 529 debuglog("Exiting region overlap with val: %d\n",result); 530 531 if (result == SPL_DISJOINT) { 532 return 0; 533 } else { 534 return 1; 535 } 536 537 return (result); 538} 539 540/* 541 * region_compare(): Examine lock regions and split appropriately 542 * 543 * XXX: Fix 64 bit overflow problems 544 * XXX: Check to make sure I got *ALL* the cases. 545 * XXX: This DESPERATELY needs a regression test. 546 */ 547enum split_status 548region_compare(starte, lene, startu, lenu, 549 start1, len1, start2, len2) 550 const u_int64_t starte, lene, startu, lenu; 551 u_int64_t *start1, *len1, *start2, *len2; 552{ 553 /* 554 * Please pay attention to the sequential exclusions 555 * of the if statements!!! 556 */ 557 enum LFLAGS lflags = LEDGE_LEFT; 558 enum RFLAGS rflags; 559 enum split_status retval; 560 561 retval = SPL_DISJOINT; 562 563 if (lene == 0 && lenu == 0) { 564 /* Examine left edge of locker */ 565 if (startu < starte) { 566 lflags = LEDGE_LEFT; 567 } else if (startu == starte) { 568 lflags = LEDGE_LBOUNDARY; 569 } else { 570 lflags = LEDGE_INSIDE; 571 } 572 573 // rflags = REDGE_RBOUNDARY; /* Both are infiinite */ 574 575 if (lflags == LEDGE_INSIDE) { 576 *start1 = starte; 577 *len1 = startu - starte; 578 } 579 580 if (lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) { 581 retval = SPL_CONTAINED; 582 } else { 583 retval = SPL_LOCK1; 584 } 585 } else if (lene == 0 && lenu != 0) { 586 /* Established lock is infinite */ 587 /* Examine left edge of unlocker */ 588 if (startu < starte) { 589 lflags = LEDGE_LEFT; 590 } else if (startu == starte) { 591 lflags = LEDGE_LBOUNDARY; 592 } else if (startu > starte) { 593 lflags = LEDGE_INSIDE; 594 } 595 596 /* Examine right edge of unlocker */ 597 if (startu + lenu < starte) { 598 /* Right edge of unlocker left of established lock */ 599 // rflags = REDGE_LEFT; 600 return SPL_DISJOINT; 601 } else if (startu + lenu == starte) { 602 /* Right edge of unlocker on start of established lock */ 603 // rflags = REDGE_LBOUNDARY; 604 return SPL_DISJOINT; 605 } else { /* Infinifty is right of finity */ 606 /* Right edge of unlocker inside established lock */ 607 rflags = REDGE_INSIDE; 608 } 609 610 if (lflags == LEDGE_INSIDE) { 611 *start1 = starte; 612 *len1 = startu - starte; 613 retval |= SPL_LOCK1; 614 } 615 616 if (rflags == REDGE_INSIDE) { 617 /* Create right lock */ 618 *start2 = startu+lenu; 619 *len2 = 0; 620 retval |= SPL_LOCK2; 621 } 622 } else if (lene != 0 && lenu == 0) { 623 /* Unlocker is infinite */ 624 /* Examine left edge of unlocker */ 625 if (startu < starte) { 626 // lflags = LEDGE_LEFT; 627 retval = SPL_CONTAINED; 628 return retval; 629 } else if (startu == starte) { 630 // lflags = LEDGE_LBOUNDARY; 631 retval = SPL_CONTAINED; 632 return retval; 633 } else if ((startu > starte) && (startu < starte + lene - 1)) { 634 lflags = LEDGE_INSIDE; 635 } else if (startu == starte + lene - 1) { 636 lflags = LEDGE_RBOUNDARY; 637 } else { /* startu > starte + lene -1 */ 638 // lflags = LEDGE_RIGHT; 639 return SPL_DISJOINT; 640 } 641 642 // rflags = REDGE_RIGHT; /* Infinity is right of finity */ 643 644 if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { 645 *start1 = starte; 646 *len1 = startu - starte; 647 retval |= SPL_LOCK1; 648 return retval; 649 } 650 651 } else { 652 /* Both locks are finite */ 653 654 /* Examine left edge of unlocker */ 655 if (startu < starte) { 656 lflags = LEDGE_LEFT; 657 } else if (startu == starte) { 658 lflags = LEDGE_LBOUNDARY; 659 } else if ((startu > starte) && (startu < starte + lene - 1)) { 660 lflags = LEDGE_INSIDE; 661 } else if (startu == starte + lene - 1) { 662 lflags = LEDGE_RBOUNDARY; 663 } else { /* startu > starte + lene -1 */ 664 // lflags = LEDGE_RIGHT; 665 return SPL_DISJOINT; 666 } 667 668 /* Examine right edge of unlocker */ 669 if (startu + lenu < starte) { 670 /* Right edge of unlocker left of established lock */ 671 // rflags = REDGE_LEFT; 672 return SPL_DISJOINT; 673 } else if (startu + lenu == starte) { 674 /* Right edge of unlocker on start of established lock */ 675 // rflags = REDGE_LBOUNDARY; 676 return SPL_DISJOINT; 677 } else if (startu + lenu < starte + lene) { 678 /* Right edge of unlocker inside established lock */ 679 rflags = REDGE_INSIDE; 680 } else if (startu + lenu == starte + lene) { 681 /* Right edge of unlocker on right edge of established lock */ 682 rflags = REDGE_RBOUNDARY; 683 } else { /* startu + lenu > starte + lene */ 684 /* Right edge of unlocker is right of established lock */ 685 rflags = REDGE_RIGHT; 686 } 687 688 if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { 689 /* Create left lock */ 690 *start1 = starte; 691 *len1 = (startu - starte); 692 retval |= SPL_LOCK1; 693 } 694 695 if (rflags == REDGE_INSIDE) { 696 /* Create right lock */ 697 *start2 = startu+lenu; 698 *len2 = starte+lene-(startu+lenu); 699 retval |= SPL_LOCK2; 700 } 701 702 if ((lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) && 703 (rflags == REDGE_RBOUNDARY || rflags == REDGE_RIGHT)) { 704 retval = SPL_CONTAINED; 705 } 706 } 707 708 return retval; 709} 710 711/* 712 * same_netobj: Compares the apprpriate bits of a netobj for identity 713 */ 714int 715same_netobj(const netobj *n0, const netobj *n1) 716{ 717 int retval; 718 719 retval = 0; 720 721 debuglog("Entering netobj identity check\n"); 722 723 if (n0->n_len == n1->n_len) { 724 debuglog("Preliminary length check passed\n"); 725 retval = !bcmp(n0->n_bytes, n1->n_bytes, n0->n_len); 726 debuglog("netobj %smatch\n", retval ? "" : "mis"); 727 } 728 729 return (retval); 730} 731 732/* 733 * same_filelock_identity: Compares the appropriate bits of a file_lock 734 */ 735int 736same_filelock_identity(fl0, fl1) 737 const struct file_lock *fl0, *fl1; 738{ 739 int retval; 740 741 debuglog("Checking filelock identity\n"); 742 743 /* 744 * Check process ids and host information. 745 */ 746 retval = (fl0->client.svid == fl1->client.svid && 747 same_netobj(&(fl0->client.oh), &(fl1->client.oh))); 748 749 debuglog("Exiting checking filelock identity: retval: %d\n",retval); 750 751 return (retval); 752} 753 754/* 755 * Below here are routines associated with manipulating the NFS 756 * lock list. 757 */ 758 759/* 760 * get_lock_matching_unlock: Return a lock which matches the given unlock lock 761 * or NULL otherwise 762 * XXX: It is a shame that this duplicates so much code from test_nfslock. 763 */ 764struct file_lock * 765get_lock_matching_unlock(const struct file_lock *fl, int cleanup) 766{ 767 struct file_lock *ifl; /* Iterator */ 768 769 debuglog("Entering lock_matching_unlock\n"); 770 debuglog("********Dump of fl*****************\n"); 771 dump_filelock(fl); 772 773 LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 774 debuglog("Pointer to file lock: %p\n",ifl); 775 776 debuglog("****Dump of ifl****\n"); 777 dump_filelock(ifl); 778 debuglog("*******************\n"); 779 780 if (cleanup && (ifl == fl)) /* don't match the lock we're cleaning up under */ 781 continue; 782 783 /* 784 * XXX: It is conceivable that someone could use the NLM RPC 785 * system to directly access filehandles. This may be a 786 * security hazard as the filehandle code may bypass normal 787 * file access controls 788 */ 789 if (fl->filehandle.n_len != ifl->filehandle.n_len) 790 continue; 791 if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes, 792 fl->filehandle.n_len)) 793 continue; 794 795 debuglog("matching_unlock: Filehandles match, " 796 "checking regions\n"); 797 798 /* Filehandles match, check for region overlap */ 799 if (!regions_overlap(fl->client.l_offset, fl->client.l_len, 800 ifl->client.l_offset, ifl->client.l_len)) 801 continue; 802 803 debuglog("matching_unlock: Region overlap" 804 " found %llu : %llu -- %llu : %llu\n", 805 fl->client.l_offset,fl->client.l_len, 806 ifl->client.l_offset,ifl->client.l_len); 807 808 /* Regions overlap, check the identity */ 809 if (!same_filelock_identity(fl,ifl)) 810 continue; 811 812 debuglog("matching_unlock: Duplicate lock id. Granting\n"); 813 return (ifl); 814 } 815 816 debuglog("Exiting lock_matching_unlock\n"); 817 818 return (NULL); 819} 820 821/* 822 * test_nfslock: check for NFS lock in lock list 823 * 824 * This routine makes the following assumptions: 825 * 1) Nothing will adjust the lock list during a lookup 826 * 827 * This routine has an intersting quirk which bit me hard. 828 * The conflicting_fl is the pointer to the conflicting lock. 829 * However, to modify the "*pointer* to the conflicting lock" rather 830 * that the "conflicting lock itself" one must pass in a "pointer to 831 * the pointer of the conflicting lock". Gross. 832 */ 833 834enum nfslock_status 835test_nfslock(const struct file_lock *fl, struct file_lock **conflicting_fl) 836{ 837 struct file_lock *ifl; /* Iterator */ 838 enum nfslock_status retval; 839 840 debuglog("Entering test_nfslock\n"); 841 842 retval = NFS_GRANTED; 843 (*conflicting_fl) = NULL; 844 845 debuglog("Entering lock search loop\n"); 846 847 debuglog("***********************************\n"); 848 debuglog("Dumping match filelock\n"); 849 debuglog("***********************************\n"); 850 dump_filelock(fl); 851 debuglog("***********************************\n"); 852 853 LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 854 if (retval == NFS_DENIED) 855 break; 856 857 debuglog("Top of lock loop\n"); 858 debuglog("Pointer to file lock: %p\n",ifl); 859 860 debuglog("***********************************\n"); 861 debuglog("Dumping test filelock\n"); 862 debuglog("***********************************\n"); 863 dump_filelock(ifl); 864 debuglog("***********************************\n"); 865 866 /* 867 * XXX: It is conceivable that someone could use the NLM RPC 868 * system to directly access filehandles. This may be a 869 * security hazard as the filehandle code may bypass normal 870 * file access controls 871 */ 872 if (fl->filehandle.n_len != ifl->filehandle.n_len) 873 continue; 874 if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes, 875 fl->filehandle.n_len)) 876 continue; 877 878 debuglog("test_nfslock: filehandle match found\n"); 879 880 /* Filehandles match, check for region overlap */ 881 if (!regions_overlap(fl->client.l_offset, fl->client.l_len, 882 ifl->client.l_offset, ifl->client.l_len)) 883 continue; 884 885 debuglog("test_nfslock: Region overlap found" 886 " %llu : %llu -- %llu : %llu\n", 887 fl->client.l_offset,fl->client.l_len, 888 ifl->client.l_offset,ifl->client.l_len); 889 890 /* Regions overlap, check the exclusivity */ 891 if (!(fl->client.exclusive || ifl->client.exclusive)) 892 continue; 893 894 debuglog("test_nfslock: Exclusivity failure: %d %d\n", 895 fl->client.exclusive, 896 ifl->client.exclusive); 897 898 if (same_filelock_identity(fl,ifl)) { 899 debuglog("test_nfslock: Duplicate id. Granting\n"); 900 (*conflicting_fl) = ifl; 901 retval = NFS_GRANTED_DUPLICATE; 902 } else { 903 /* locking attempt fails */ 904 debuglog("test_nfslock: Lock attempt failed\n"); 905 debuglog("Desired lock\n"); 906 dump_filelock(fl); 907 debuglog("Conflicting lock\n"); 908 dump_filelock(ifl); 909 (*conflicting_fl) = ifl; 910 retval = NFS_DENIED; 911 } 912 } 913 914 debuglog("Dumping file locks\n"); 915 debuglog("Exiting test_nfslock\n"); 916 917 return (retval); 918} 919 920/* 921 * lock_nfslock: attempt to create a lock in the NFS lock list 922 * 923 * This routine tests whether the lock will be granted and then adds 924 * the entry to the lock list if so. 925 * 926 * Argument fl gets modified as its list housekeeping entries get modified 927 * upon insertion into the NFS lock list 928 * 929 * This routine makes several assumptions: 930 * 1) It is perfectly happy to grant a duplicate lock from the same pid. 931 * While this seems to be intuitively wrong, it is required for proper 932 * Posix semantics during unlock. It is absolutely imperative to not 933 * unlock the main lock before the two child locks are established. Thus, 934 * one has be be able to create duplicate locks over an existing lock 935 * 2) It currently accepts duplicate locks from the same id,pid 936 */ 937 938enum nfslock_status 939lock_nfslock(struct file_lock *fl) 940{ 941 enum nfslock_status retval; 942 struct file_lock *dummy_fl; 943 944 dummy_fl = NULL; 945 946 debuglog("Entering lock_nfslock...\n"); 947 948 retval = test_nfslock(fl,&dummy_fl); 949 950 if (retval == NFS_GRANTED || retval == NFS_GRANTED_DUPLICATE) { 951 debuglog("Inserting lock...\n"); 952 dump_filelock(fl); 953 LIST_INSERT_HEAD(&nfslocklist_head, fl, nfslocklist); 954 } 955 956 debuglog("Exiting lock_nfslock...\n"); 957 958 return (retval); 959} 960 961/* 962 * delete_nfslock: delete an NFS lock list entry 963 * 964 * This routine is used to delete a lock out of the NFS lock list 965 * without regard to status, underlying locks, regions or anything else 966 * 967 * Note that this routine *does not deallocate memory* of the lock. 968 * It just disconnects it from the list. The lock can then be used 969 * by other routines without fear of trashing the list. 970 */ 971 972enum nfslock_status 973delete_nfslock(struct file_lock *fl) 974{ 975 976 LIST_REMOVE(fl, nfslocklist); 977 978 return (NFS_GRANTED); 979} 980 981enum split_status 982split_nfslock(exist_lock, unlock_lock, left_lock, right_lock) 983 const struct file_lock *exist_lock, *unlock_lock; 984 struct file_lock **left_lock, **right_lock; 985{ 986 u_int64_t start1, len1, start2, len2; 987 enum split_status spstatus; 988 989 spstatus = region_compare(exist_lock->client.l_offset, exist_lock->client.l_len, 990 unlock_lock->client.l_offset, unlock_lock->client.l_len, 991 &start1, &len1, &start2, &len2); 992 993 if ((spstatus & SPL_LOCK1) != 0) { 994 *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name); 995 if (*left_lock == NULL) { 996 debuglog("Unable to allocate resource for split 1\n"); 997 return SPL_RESERR; 998 } 999 1000 fill_file_lock(*left_lock, 1001 exist_lock->client.exclusive, exist_lock->client.svid, 1002 start1, len1, 1003 exist_lock->nsm_status, 1004 exist_lock->status, exist_lock->flags, exist_lock->blocking); 1005 } 1006 1007 if ((spstatus & SPL_LOCK2) != 0) { 1008 *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name); 1009 if (*right_lock == NULL) { 1010 debuglog("Unable to allocate resource for split 1\n"); 1011 if (*left_lock != NULL) { 1012 deallocate_file_lock(*left_lock); 1013 } 1014 return SPL_RESERR; 1015 } 1016 1017 fill_file_lock(*right_lock, 1018 exist_lock->client.exclusive, exist_lock->client.svid, 1019 start2, len2, 1020 exist_lock->nsm_status, 1021 exist_lock->status, exist_lock->flags, exist_lock->blocking); 1022 } 1023 1024 return spstatus; 1025} 1026 1027enum nfslock_status 1028unlock_nfslock(fl, released_lock, left_lock, right_lock, cleanup) 1029 const struct file_lock *fl; 1030 struct file_lock **released_lock; 1031 struct file_lock **left_lock; 1032 struct file_lock **right_lock; 1033 int cleanup; 1034{ 1035 struct file_lock *mfl; /* Matching file lock */ 1036 enum nfslock_status retval; 1037 enum split_status spstatus; 1038 1039 debuglog("Entering unlock_nfslock\n"); 1040 1041 *released_lock = NULL; 1042 *left_lock = NULL; 1043 *right_lock = NULL; 1044 1045 retval = NFS_DENIED_NOLOCK; 1046 1047 debuglog("Attempting to match lock...\n"); 1048 mfl = get_lock_matching_unlock(fl, cleanup); 1049 1050 if (mfl != NULL) { 1051 debuglog("Unlock matched. Querying for split\n"); 1052 1053 spstatus = split_nfslock(mfl, fl, left_lock, right_lock); 1054 1055 debuglog("Split returned %d %p %p %p %p\n",spstatus,mfl,fl,*left_lock,*right_lock); 1056 debuglog("********Split dumps********"); 1057 dump_filelock(mfl); 1058 dump_filelock(fl); 1059 dump_filelock(*left_lock); 1060 dump_filelock(*right_lock); 1061 debuglog("********End Split dumps********"); 1062 1063 if (spstatus == SPL_RESERR) { 1064 if (*left_lock != NULL) { 1065 deallocate_file_lock(*left_lock); 1066 *left_lock = NULL; 1067 } 1068 1069 if (*right_lock != NULL) { 1070 deallocate_file_lock(*right_lock); 1071 *right_lock = NULL; 1072 } 1073 1074 return NFS_RESERR; 1075 } 1076 1077 /* Insert new locks from split if required */ 1078 if (*left_lock != NULL) { 1079 debuglog("Split left activated\n"); 1080 LIST_INSERT_HEAD(&nfslocklist_head, *left_lock, nfslocklist); 1081 } 1082 1083 if (*right_lock != NULL) { 1084 debuglog("Split right activated\n"); 1085 LIST_INSERT_HEAD(&nfslocklist_head, *right_lock, nfslocklist); 1086 } 1087 1088 /* Unlock the lock since it matches identity */ 1089 LIST_REMOVE(mfl, nfslocklist); 1090 *released_lock = mfl; 1091 retval = NFS_GRANTED; 1092 } 1093 1094 debuglog("Exiting unlock_nfslock\n"); 1095 1096 return retval; 1097} 1098 1099/* 1100 * Below here are the routines for manipulating the file lock directly 1101 * on the disk hardware itself 1102 */ 1103enum hwlock_status 1104lock_hwlock(struct file_lock *fl) 1105{ 1106 struct monfile *imf,*nmf; 1107 int lflags, flerror; 1108 fhandle_t fh; 1109 1110 /* Scan to see if filehandle already present */ 1111 LIST_FOREACH(imf, &monfilelist_head, monfilelist) { 1112 if ((fl->filehandle.n_len == imf->filehandle.n_len) && 1113 (bcmp(fl->filehandle.n_bytes, imf->filehandle.n_bytes, 1114 fl->filehandle.n_len) == 0)) { 1115 /* imf is the correct filehandle */ 1116 break; 1117 } 1118 } 1119 1120 /* 1121 * Filehandle already exists (we control the file) 1122 * *AND* NFS has already cleared the lock for availability 1123 * Grant it and bump the refcount. 1124 */ 1125 if (imf != NULL) { 1126 ++(imf->refcount); 1127 return (HW_GRANTED); 1128 } 1129 1130 /* No filehandle found, create and go */ 1131 nmf = malloc(sizeof(struct monfile)); 1132 if (nmf == NULL) { 1133 debuglog("hwlock resource allocation failure\n"); 1134 return (HW_RESERR); 1135 } 1136 nmf->filehandle.n_bytes = malloc(fl->filehandle.n_len); 1137 if (nmf->filehandle.n_bytes == NULL) { 1138 debuglog("hwlock resource allocation failure\n"); 1139 free(nmf); 1140 return (HW_RESERR); 1141 } 1142 1143 if (fl->filehandle.n_len > NFSV3_MAX_FH_SIZE) { 1144 debuglog("hwlock: bad fh length %d (from %16s): %32s\n", 1145 fl->filehandle.n_len, fl->client_name, strerror(errno)); 1146 free(nmf->filehandle.n_bytes); 1147 free(nmf); 1148 return (HW_STALEFH); 1149 } 1150 fh.fh_len = fl->filehandle.n_len; 1151 bcopy(fl->filehandle.n_bytes, fh.fh_data, fh.fh_len); 1152 1153 /* O_RDWR may not work if file system is read-only */ 1154 nmf->fd = fhopen(&fh, O_RDWR); 1155 if ((nmf->fd < 0) && (errno == EROFS) && !fl->client.exclusive) 1156 nmf->fd = fhopen(&fh, O_RDONLY); 1157 if (nmf->fd < 0) { 1158 debuglog("fhopen failed (from %16s): %32s\n", 1159 fl->client_name, strerror(errno)); 1160 free(nmf->filehandle.n_bytes); 1161 free(nmf); 1162 switch (errno) { 1163 case ESTALE: 1164 return (HW_STALEFH); 1165 case EROFS: 1166 return (HW_READONLY); 1167 default: 1168 return (HW_RESERR); 1169 } 1170 } 1171 1172 /* File opened correctly, fill the monitor struct */ 1173 nmf->filehandle.n_len = fl->filehandle.n_len; 1174 bcopy(fl->filehandle.n_bytes, nmf->filehandle.n_bytes, fl->filehandle.n_len); 1175 nmf->refcount = 1; 1176 nmf->exclusive = fl->client.exclusive; 1177 1178 lflags = (nmf->exclusive == 1) ? 1179 (LOCK_EX | LOCK_NB) : (LOCK_SH | LOCK_NB); 1180 1181 flerror = flock(nmf->fd, lflags); 1182 1183 if (flerror != 0) { 1184 debuglog("flock failed (from %16s): %32s\n", 1185 fl->client_name, strerror(errno)); 1186 close(nmf->fd); 1187 free(nmf->filehandle.n_bytes); 1188 free(nmf); 1189 switch (errno) { 1190 case EAGAIN: 1191 return (HW_DENIED); 1192 case ESTALE: 1193 return (HW_STALEFH); 1194 case EROFS: 1195 return (HW_READONLY); 1196 default: 1197 return (HW_RESERR); 1198 break; 1199 } 1200 } 1201 1202 /* File opened and locked */ 1203 LIST_INSERT_HEAD(&monfilelist_head, nmf, monfilelist); 1204 1205 debuglog("flock succeeded (from %16s)\n", fl->client_name); 1206 return (HW_GRANTED); 1207} 1208 1209enum hwlock_status 1210unlock_hwlock(const struct file_lock *fl) 1211{ 1212 struct monfile *imf; 1213 1214 debuglog("Entering unlock_hwlock\n"); 1215 debuglog("Entering loop interation\n"); 1216 1217 /* Scan to see if filehandle already present */ 1218 LIST_FOREACH(imf, &monfilelist_head, monfilelist) { 1219 if ((fl->filehandle.n_len == imf->filehandle.n_len) && 1220 (bcmp(fl->filehandle.n_bytes, imf->filehandle.n_bytes, 1221 fl->filehandle.n_len) == 0)) { 1222 /* imf is the correct filehandle */ 1223 break; 1224 } 1225 } 1226 1227 debuglog("Completed iteration. Proceeding\n"); 1228 1229 if (imf == NULL) { 1230 /* No lock found */ 1231 debuglog("Exiting unlock_hwlock (HW_DENIED_NOLOCK)\n"); 1232 return (HW_DENIED_NOLOCK); 1233 } 1234 1235 /* Lock found */ 1236 --imf->refcount; 1237 1238 if (imf->refcount < 0) { 1239 debuglog("Negative hardware reference count\n"); 1240 } 1241 1242 if (imf->refcount <= 0) { 1243 close(imf->fd); 1244 LIST_REMOVE(imf, monfilelist); 1245 free(imf->filehandle.n_bytes); 1246 free(imf); 1247 } 1248 debuglog("Exiting unlock_hwlock (HW_GRANTED)\n"); 1249 return (HW_GRANTED); 1250} 1251 1252enum hwlock_status 1253test_hwlock(fl, conflicting_fl) 1254 const struct file_lock *fl __unused; 1255 struct file_lock **conflicting_fl __unused; 1256{ 1257 1258 /* 1259 * XXX: lock tests on hardware are not required until 1260 * true partial file testing is done on the underlying file 1261 */ 1262 return (HW_RESERR); 1263} 1264 1265 1266 1267/* 1268 * Below here are routines for manipulating blocked lock requests 1269 * They should only be called from the XXX_partialfilelock routines 1270 * if at all possible 1271 */ 1272 1273void 1274add_blockingfilelock(struct file_lock *fl) 1275{ 1276 struct file_lock *ifl, *nfl; 1277 1278 debuglog("Entering add_blockingfilelock\n"); 1279 1280 /* 1281 * Check for a duplicate lock request. 1282 * If found, deallocate the older request. 1283 */ 1284 ifl = LIST_FIRST(&blockedlocklist_head); 1285 for (; ifl != NULL; ifl = nfl) { 1286 debuglog("Pointer to file lock: %p\n",ifl); 1287 debuglog("****Dump of ifl****\n"); 1288 dump_filelock(ifl); 1289 debuglog("*******************\n"); 1290 1291 nfl = LIST_NEXT(ifl, nfslocklist); 1292 1293 if (fl->filehandle.n_len != ifl->filehandle.n_len) 1294 continue; 1295 if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes, 1296 fl->filehandle.n_len)) 1297 continue; 1298 1299 /* Filehandles match, check region */ 1300 if ((fl->client.l_offset != ifl->client.l_offset) || 1301 (fl->client.l_len != ifl->client.l_len)) 1302 continue; 1303 1304 /* Regions match, check the identity */ 1305 if (!same_filelock_identity(fl,ifl)) 1306 continue; 1307 1308 debuglog("add_blockingfilelock: removing duplicate lock request.\n"); 1309 remove_blockingfilelock(ifl); 1310 deallocate_file_lock(ifl); 1311 break; 1312 } 1313 1314 /* 1315 * Clear the blocking flag so that it can be reused without 1316 * adding it to the blocking queue a second time 1317 */ 1318 1319 fl->blocking = 0; 1320 LIST_INSERT_HEAD(&blockedlocklist_head, fl, nfslocklist); 1321 1322 debuglog("Exiting add_blockingfilelock\n"); 1323} 1324 1325void 1326remove_blockingfilelock(struct file_lock *fl) 1327{ 1328 1329 debuglog("Entering remove_blockingfilelock\n"); 1330 1331 LIST_REMOVE(fl, nfslocklist); 1332 1333 debuglog("Exiting remove_blockingfilelock\n"); 1334} 1335 1336void 1337clear_blockingfilelock(const char *hostname) 1338{ 1339 struct file_lock *ifl,*nfl; 1340 1341 /* 1342 * Normally, LIST_FOREACH is called for, but since 1343 * the current element *is* the iterator, deleting it 1344 * would mess up the iteration. Thus, a next element 1345 * must be used explicitly 1346 */ 1347 1348 ifl = LIST_FIRST(&blockedlocklist_head); 1349 1350 while (ifl != NULL) { 1351 nfl = LIST_NEXT(ifl, nfslocklist); 1352 1353 if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { 1354 remove_blockingfilelock(ifl); 1355 deallocate_file_lock(ifl); 1356 } 1357 1358 ifl = nfl; 1359 } 1360} 1361 1362int need_retry_blocked_locks = 0; /* need to call retry_blockingfilelocklist() */ 1363 1364void 1365retry_blockingfilelocklist(netobj *fh) 1366{ 1367 /* 1368 * If fh is given, then retry just the locks with the 1369 * same filehandle in the blocked list. 1370 * Otherwise, simply retry all locks in the blocked list. 1371 */ 1372 struct file_lock *ifl, *nfl, *pfl; /* Iterator */ 1373 enum partialfilelock_status pflstatus; 1374 int rv; 1375 1376 debuglog("Entering retry_blockingfilelocklist\n"); 1377 1378 need_retry_blocked_locks = 0; 1379 1380 pfl = NULL; 1381 ifl = LIST_FIRST(&blockedlocklist_head); 1382 debuglog("Iterator choice %p\n",ifl); 1383 1384 while (ifl != NULL) { 1385 /* 1386 * SUBTLE BUG: The next element must be worked out before the 1387 * current element has been moved 1388 */ 1389 nfl = LIST_NEXT(ifl, nfslocklist); 1390 debuglog("Iterator choice %p\n",ifl); 1391 debuglog("Prev iterator choice %p\n",pfl); 1392 debuglog("Next iterator choice %p\n",nfl); 1393 1394 /* if given a filehandle, only retry locks for the same filehandle */ 1395 if (fh && !same_netobj(fh, &ifl->filehandle)) { 1396 ifl = nfl; 1397 continue; 1398 } 1399 1400 /* 1401 * SUBTLE BUG: The file_lock must be removed from the 1402 * old list so that it's list pointers get disconnected 1403 * before being allowed to participate in the new list 1404 * which will automatically add it in if necessary. 1405 */ 1406 1407 LIST_REMOVE(ifl, nfslocklist); 1408 pflstatus = lock_partialfilelock(ifl); 1409 1410 if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) { 1411 debuglog("Granted blocked lock\n"); 1412 /* lock granted and is now being used */ 1413 rv = send_granted(ifl, 0); 1414 if (rv) { 1415 /* 1416 * Uh oh... the NLM_GRANTED message failed. 1417 * About the only thing we can do is drop the lock. 1418 * Note: this could be bad if the error was only 1419 * transient. Hopefully, if the client is still 1420 * waiting for the lock, they will resend the request. 1421 */ 1422 do_unlock(ifl); 1423 /* ifl is NO LONGER VALID AT THIS POINT */ 1424 } 1425 } else if (pflstatus == PFL_HWDENIED_STALEFH) { 1426 /* 1427 * Uh oh... 1428 * It would be nice if we could inform the client of 1429 * this error. Unfortunately, there's no way to do 1430 * that in the NLM protocol (can't send "granted" 1431 * message with an error and there's no "never going 1432 * to be granted" message). 1433 * 1434 * Since there's no chance of this blocked request ever 1435 * succeeding, we drop the lock request rather than 1436 * needlessly keeping it around just to rot forever in 1437 * the blocked lock list. 1438 * 1439 * Hopefully, if the client is still waiting for the lock, 1440 * they will resend the request (and get an error then). 1441 * 1442 * XXX Note: PFL_HWDENIED_READONLY could potentially 1443 * be handled this way as well, although that would 1444 * only be an issue if a file system changed from 1445 * read-write to read-only out from under a blocked 1446 * lock request, and that's far less likely than a 1447 * file disappearing out from under such a request. 1448 */ 1449 deallocate_file_lock(ifl); 1450 /* ifl is NO LONGER VALID AT THIS POINT */ 1451 } else { 1452 /* Reinsert lock back into same place in blocked list */ 1453 debuglog("Replacing blocked lock\n"); 1454 if (pfl != NULL) 1455 LIST_INSERT_AFTER(pfl, ifl, nfslocklist); 1456 else 1457 /* ifl is the only elem. in the list */ 1458 LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist); 1459 } 1460 1461 if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE || 1462 pflstatus == PFL_HWDENIED_STALEFH) { 1463 /* If ifl was permanently removed from the list, (e.g it */ 1464 /* was granted or dropped), pfl should remain where it's at. */ 1465 } else { 1466 /* If ifl was left in the list, (e.g it was reinserted back */ 1467 /* in place), pfl should simply be moved forward to be ifl */ 1468 pfl = ifl; 1469 } 1470 /* Valid increment behavior regardless of state of ifl */ 1471 ifl = nfl; 1472 } 1473 1474 debuglog("Exiting retry_blockingfilelocklist\n"); 1475} 1476 1477/* 1478 * Below here are routines associated with manipulating all 1479 * aspects of the partial file locking system (list, hardware, etc.) 1480 */ 1481 1482/* 1483 * Please note that lock monitoring must be done at this level which 1484 * keeps track of *individual* lock requests on lock and unlock 1485 * 1486 * XXX: Split unlocking is going to make the unlock code miserable 1487 */ 1488 1489/* 1490 * lock_partialfilelock: 1491 * 1492 * Argument fl gets modified as its list housekeeping entries get modified 1493 * upon insertion into the NFS lock list 1494 * 1495 * This routine makes several assumptions: 1496 * 1) It (will) pass locks through to flock to lock the entire underlying file 1497 * and then parcel out NFS locks if it gets control of the file. 1498 * This matches the old rpc.lockd file semantics (except where it 1499 * is now more correct). It is the safe solution, but will cause 1500 * overly restrictive blocking if someone is trying to use the 1501 * underlying files without using NFS. This appears to be an 1502 * acceptable tradeoff since most people use standalone NFS servers. 1503 * XXX: The right solution is probably kevent combined with fcntl 1504 * 1505 * 2) Nothing modifies the lock lists between testing and granting 1506 * I have no idea whether this is a useful assumption or not 1507 */ 1508 1509enum partialfilelock_status 1510lock_partialfilelock(struct file_lock *fl) 1511{ 1512 enum partialfilelock_status retval; 1513 enum nfslock_status lnlstatus; 1514 enum hwlock_status hwstatus; 1515 1516 debuglog("Entering lock_partialfilelock\n"); 1517 1518 retval = PFL_DENIED; 1519 1520 /* 1521 * Execute the NFS lock first, if possible, as it is significantly 1522 * easier and less expensive to undo than the filesystem lock 1523 */ 1524 1525 lnlstatus = lock_nfslock(fl); 1526 1527 switch (lnlstatus) { 1528 case NFS_GRANTED: 1529 case NFS_GRANTED_DUPLICATE: 1530 /* 1531 * At this point, the NFS lock is allocated and active. 1532 * Remember to clean it up if the hardware lock fails 1533 */ 1534 hwstatus = lock_hwlock(fl); 1535 1536 switch (hwstatus) { 1537 case HW_GRANTED: 1538 case HW_GRANTED_DUPLICATE: 1539 debuglog("HW GRANTED\n"); 1540 /* 1541 * XXX: Fixme: Check hwstatus for duplicate when 1542 * true partial file locking and accounting is 1543 * done on the hardware 1544 */ 1545 if (lnlstatus == NFS_GRANTED_DUPLICATE) { 1546 retval = PFL_GRANTED_DUPLICATE; 1547 } else { 1548 retval = PFL_GRANTED; 1549 } 1550 if (fl->flags & LOCK_MON) 1551 monitor_lock_host_by_name(fl->client_name, fl->addr); 1552 break; 1553 case HW_RESERR: 1554 debuglog("HW RESERR\n"); 1555 retval = PFL_HWRESERR; 1556 break; 1557 case HW_DENIED: 1558 debuglog("HW DENIED\n"); 1559 retval = PFL_HWDENIED; 1560 break; 1561 case HW_DENIED_NOLOCK: 1562 debuglog("HW DENIED NOLOCK\n"); 1563 retval = PFL_HWDENIED_NOLOCK; 1564 break; 1565 case HW_STALEFH: 1566 debuglog("HW STALE FH\n"); 1567 retval = PFL_HWDENIED_STALEFH; 1568 break; 1569 case HW_READONLY: 1570 debuglog("HW READONLY\n"); 1571 retval = PFL_HWDENIED_READONLY; 1572 break; 1573 default: 1574 debuglog("Unmatched hwstatus %d\n",hwstatus); 1575 break; 1576 } 1577 1578 if (retval != PFL_GRANTED && 1579 retval != PFL_GRANTED_DUPLICATE) { 1580 /* Clean up the NFS lock */ 1581 debuglog("Deleting trial NFS lock\n"); 1582 delete_nfslock(fl); 1583 } 1584 if (retval == PFL_GRANTED_DUPLICATE) { 1585 /* Clean up locks replaced by this lock */ 1586 debuglog("Cleaning up replaced locks\n"); 1587 unlock_partialfilelock(fl, 1); 1588 } 1589 break; 1590 case NFS_DENIED: 1591 retval = PFL_NFSDENIED; 1592 break; 1593 case NFS_RESERR: 1594 retval = PFL_NFSRESERR; 1595 break; 1596 default: 1597 debuglog("Unmatched lnlstatus %d\n"); 1598 retval = PFL_NFSDENIED_NOLOCK; 1599 break; 1600 } 1601 1602 /* 1603 * By the time fl reaches here, it is completely free again on 1604 * failure. The NFS lock done before attempting the 1605 * hardware lock has been backed out 1606 */ 1607 1608 if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) { 1609 /* Once last chance to check the lock */ 1610 if (fl->blocking == 1) { 1611 if (retval == PFL_NFSDENIED) { 1612 /* Queue the lock */ 1613 debuglog("BLOCKING LOCK RECEIVED\n"); 1614 retval = PFL_NFSBLOCKED; 1615 add_blockingfilelock(fl); 1616 dump_filelock(fl); 1617 } else { 1618 /* retval is okay as PFL_HWDENIED */ 1619 debuglog("BLOCKING LOCK DENIED IN HARDWARE\n"); 1620 dump_filelock(fl); 1621 } 1622 } else { 1623 /* Leave retval alone, it's already correct */ 1624 debuglog("Lock denied. Non-blocking failure\n"); 1625 dump_filelock(fl); 1626 } 1627 } 1628 1629 debuglog("Exiting lock_partialfilelock\n"); 1630 1631 return retval; 1632} 1633 1634/* 1635 * unlock_partialfilelock: 1636 * 1637 * Given a file_lock, unlock all locks which match. 1638 * 1639 * Note that a given lock might have to unlock ITSELF! See 1640 * clear_partialfilelock for example. 1641 * 1642 * If cleanup is set, we will NOT unlock the lock passed in. 1643 * We will merely clean up all other locks that the given lock 1644 * has replaced. 1645 */ 1646 1647enum partialfilelock_status 1648unlock_partialfilelock(const struct file_lock *fl, int cleanup) 1649{ 1650 struct file_lock *lfl,*rfl,*releasedfl,*selffl; 1651 enum partialfilelock_status retval; 1652 enum nfslock_status unlstatus; 1653 enum hwlock_status unlhwstatus, lhwstatus; 1654 1655 debuglog("Entering unlock_partialfilelock\n"); 1656 1657 selffl = NULL; 1658 lfl = NULL; 1659 rfl = NULL; 1660 releasedfl = NULL; 1661 1662 /* 1663 * There are significant overlap and atomicity issues 1664 * with partially releasing a lock. For example, releasing 1665 * part of an NFS shared lock does *not* always release the 1666 * corresponding part of the file since there is only one 1667 * rpc.lockd UID but multiple users could be requesting it 1668 * from NFS. Also, an unlock request should never allow 1669 * another process to gain a lock on the remaining parts. 1670 * ie. Always apply the new locks before releasing the 1671 * old one 1672 */ 1673 1674 /* 1675 * Loop is required since multiple little locks 1676 * can be allocated and then deallocated with one 1677 * big unlock. 1678 * 1679 * The loop is required to be here so that the nfs & 1680 * hw subsystems do not need to communicate with one 1681 * one another 1682 */ 1683 1684 do { 1685 debuglog("Value of releasedfl: %p\n",releasedfl); 1686 /* lfl&rfl are created *AND* placed into the NFS lock list if required */ 1687 unlstatus = unlock_nfslock(fl, &releasedfl, &lfl, &rfl, cleanup); 1688 debuglog("Value of releasedfl: %p\n",releasedfl); 1689 1690 1691 /* XXX: This is grungy. It should be refactored to be cleaner */ 1692 if (lfl != NULL) { 1693 lhwstatus = lock_hwlock(lfl); 1694 if (lhwstatus != HW_GRANTED && 1695 lhwstatus != HW_GRANTED_DUPLICATE) { 1696 debuglog("HW duplicate lock failure for left split\n"); 1697 } 1698 if (lfl->flags & LOCK_MON) 1699 monitor_lock_host_by_name(lfl->client_name, lfl->addr); 1700 } 1701 1702 if (rfl != NULL) { 1703 lhwstatus = lock_hwlock(rfl); 1704 if (lhwstatus != HW_GRANTED && 1705 lhwstatus != HW_GRANTED_DUPLICATE) { 1706 debuglog("HW duplicate lock failure for right split\n"); 1707 } 1708 if (rfl->flags & LOCK_MON) 1709 monitor_lock_host_by_name(rfl->client_name, rfl->addr); 1710 } 1711 1712 switch (unlstatus) { 1713 case NFS_GRANTED: 1714 /* Attempt to unlock on the hardware */ 1715 debuglog("NFS unlock granted. Attempting hardware unlock\n"); 1716 1717 /* This call *MUST NOT* unlock the two newly allocated locks */ 1718 unlhwstatus = unlock_hwlock(fl); 1719 debuglog("HW unlock returned with code %d\n",unlhwstatus); 1720 1721 switch (unlhwstatus) { 1722 case HW_GRANTED: 1723 debuglog("HW unlock granted\n"); 1724 if (releasedfl->flags & LOCK_MON) 1725 unmonitor_lock_host(releasedfl->client_name); 1726 retval = PFL_GRANTED; 1727 break; 1728 case HW_DENIED_NOLOCK: 1729 /* Huh?!?! This shouldn't happen */ 1730 debuglog("HW unlock denied no lock\n"); 1731 retval = PFL_HWRESERR; 1732 /* Break out of do-while */ 1733 unlstatus = NFS_RESERR; 1734 break; 1735 default: 1736 debuglog("HW unlock failed\n"); 1737 retval = PFL_HWRESERR; 1738 /* Break out of do-while */ 1739 unlstatus = NFS_RESERR; 1740 break; 1741 } 1742 1743 debuglog("Exiting with status retval: %d\n",retval); 1744 1745 // XXX sending granted messages before unlock response 1746 // XXX causes unlock response to be corrupted? 1747 // XXX Workaround is to move this to nlm_prot_svc.c 1748 // XXX after the unlock response is sent. 1749 // retry_blockingfilelocklist(); 1750 need_retry_blocked_locks = 1; 1751 break; 1752 case NFS_DENIED_NOLOCK: 1753 retval = PFL_GRANTED; 1754 debuglog("All locks cleaned out\n"); 1755 break; 1756 default: 1757 retval = PFL_NFSRESERR; 1758 debuglog("NFS unlock failure\n"); 1759 dump_filelock(fl); 1760 break; 1761 } 1762 1763 if (releasedfl != NULL) { 1764 if (fl == releasedfl) { 1765 /* 1766 * XXX: YECHHH!!! Attempt to unlock self succeeded 1767 * but we can't deallocate the space yet. This is what 1768 * happens when you don't write malloc and free together 1769 */ 1770 if (!cleanup) { 1771 debuglog("Attempt to unlock self\n"); 1772 selffl = releasedfl; 1773 } 1774 } else { 1775 /* 1776 * XXX: this deallocation *still* needs to migrate closer 1777 * to the allocation code way up in get_lock or the allocation 1778 * code needs to migrate down (violation of "When you write 1779 * malloc you must write free") 1780 */ 1781 1782 deallocate_file_lock(releasedfl); 1783 } 1784 } 1785 1786 } while (unlstatus == NFS_GRANTED); 1787 1788 if (!cleanup && (selffl != NULL)) { 1789 /* 1790 * This statement wipes out the incoming file lock (fl) 1791 * in spite of the fact that it is declared const 1792 */ 1793 debuglog("WARNING! Destroying incoming lock pointer\n"); 1794 deallocate_file_lock(selffl); 1795 } 1796 1797 debuglog("Exiting unlock_partialfilelock\n"); 1798 1799 return retval; 1800} 1801 1802/* 1803 * clear_partialfilelock 1804 * 1805 * Normally called in response to statd state number change. 1806 * Wipe out all locks held by a host. As a bonus, the act of 1807 * doing so should automatically clear their statd entries and 1808 * unmonitor the host. 1809 */ 1810 1811void 1812clear_partialfilelock(const char *hostname) 1813{ 1814 struct file_lock *ifl, *nfl; 1815 enum partialfilelock_status pfsret; 1816 struct host *ihp; 1817 1818 /* 1819 * Check if the name we got from statd is 1820 * actually one reverse-mapped from the client's 1821 * address. If so, use the name provided as 1822 * the caller_name in lock requests instead so that 1823 * we can correctly identify the client's locks. 1824 */ 1825 TAILQ_FOREACH(ihp, &hostlst_head, hostlst) { 1826 if (ihp->revname && strncmp(hostname, ihp->revname, 1827 SM_MAXSTRLEN) == 0) { 1828 hostname = ihp->name; 1829 debuglog("Clearing locks for %s (%s)\n", 1830 hostname, ihp->revname); 1831 break; 1832 } 1833 } 1834 1835 /* Clear blocking file lock list */ 1836 clear_blockingfilelock(hostname); 1837 1838 /* do all required unlocks */ 1839 /* Note that unlock can smash the current pointer to a lock */ 1840 1841 /* 1842 * Normally, LIST_FOREACH is called for, but since 1843 * the current element *is* the iterator, deleting it 1844 * would mess up the iteration. Thus, a next element 1845 * must be used explicitly 1846 */ 1847restart: 1848 ifl = LIST_FIRST(&nfslocklist_head); 1849 1850 while (ifl != NULL) { 1851 nfl = LIST_NEXT(ifl, nfslocklist); 1852 1853 if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { 1854 /* Unlock destroys ifl out from underneath */ 1855 pfsret = unlock_partialfilelock(ifl, 0); 1856 if (pfsret != PFL_GRANTED) { 1857 /* Uh oh... there was some sort of problem. */ 1858 /* If we restart the loop, we may get */ 1859 /* stuck here forever getting errors. */ 1860 /* So, let's just abort the whole scan. */ 1861 syslog(LOG_WARNING, "lock clearing for %s failed: %d", 1862 hostname, pfsret); 1863 break; 1864 } 1865 /* ifl is NO LONGER VALID AT THIS POINT */ 1866 /* Note: the unlock may deallocate several existing locks. */ 1867 /* Therefore, we need to restart the scanning of the list, */ 1868 /* because nfl could be pointing to a freed lock. */ 1869 goto restart; 1870 } 1871 ifl = nfl; 1872 } 1873} 1874 1875/* 1876 * test_partialfilelock: 1877 */ 1878enum partialfilelock_status 1879test_partialfilelock(const struct file_lock *fl, 1880 struct file_lock **conflicting_fl) 1881{ 1882 enum partialfilelock_status retval; 1883 enum nfslock_status teststatus; 1884 1885 debuglog("Entering testpartialfilelock...\n"); 1886 1887 teststatus = test_nfslock(fl, conflicting_fl); 1888 debuglog("test_partialfilelock: teststatus %d\n",teststatus); 1889 1890 if (teststatus == NFS_GRANTED || teststatus == NFS_GRANTED_DUPLICATE) { 1891 /* XXX: Add the underlying filesystem locking code */ 1892 retval = (teststatus == NFS_GRANTED) ? 1893 PFL_GRANTED : PFL_GRANTED_DUPLICATE; 1894 debuglog("Dumping locks...\n"); 1895 dump_filelock(fl); 1896 dump_filelock(*conflicting_fl); 1897 debuglog("Done dumping locks...\n"); 1898 } else { 1899 retval = PFL_NFSDENIED; 1900 debuglog("NFS test denied.\n"); 1901 dump_filelock(fl); 1902 debuglog("Conflicting.\n"); 1903 dump_filelock(*conflicting_fl); 1904 } 1905 1906 debuglog("Exiting testpartialfilelock...\n"); 1907 1908 return retval; 1909} 1910 1911/* 1912 * Below here are routines associated with translating the partial file locking 1913 * codes into useful codes to send back to the NFS RPC messaging system 1914 */ 1915 1916/* 1917 * These routines translate the (relatively) useful return codes back onto 1918 * the few return codes which the nlm subsystems wishes to trasmit 1919 */ 1920 1921enum nlm4_stats 1922do_test(struct file_lock *fl, struct file_lock **conflicting_fl) 1923{ 1924 enum partialfilelock_status pfsret; 1925 enum nlm4_stats retval; 1926 1927 debuglog("Entering do_test...\n"); 1928 1929 pfsret = test_partialfilelock(fl,conflicting_fl); 1930 1931 switch (pfsret) { 1932 case PFL_GRANTED: 1933 debuglog("PFL test lock granted\n"); 1934 dump_filelock(fl); 1935 dump_filelock(*conflicting_fl); 1936 retval = (fl->flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 1937 break; 1938 case PFL_GRANTED_DUPLICATE: 1939 debuglog("PFL test lock granted--duplicate id detected\n"); 1940 dump_filelock(fl); 1941 dump_filelock(*conflicting_fl); 1942 debuglog("Clearing conflicting_fl for call semantics\n"); 1943 *conflicting_fl = NULL; 1944 retval = (fl->flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 1945 break; 1946 case PFL_NFSDENIED: 1947 case PFL_HWDENIED: 1948 debuglog("PFL test lock denied\n"); 1949 dump_filelock(fl); 1950 dump_filelock(*conflicting_fl); 1951 retval = (fl->flags & LOCK_V4) ? nlm4_denied : (nlm4_stats) nlm_denied; 1952 break; 1953 case PFL_NFSRESERR: 1954 case PFL_HWRESERR: 1955 debuglog("PFL test lock resource fail\n"); 1956 dump_filelock(fl); 1957 dump_filelock(*conflicting_fl); 1958 retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 1959 break; 1960 default: 1961 debuglog("PFL test lock *FAILED*\n"); 1962 dump_filelock(fl); 1963 dump_filelock(*conflicting_fl); 1964 retval = (fl->flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 1965 break; 1966 } 1967 1968 debuglog("Exiting do_test...\n"); 1969 1970 return retval; 1971} 1972 1973/* 1974 * do_lock: Try to acquire a lock 1975 * 1976 * This routine makes a distinction between NLM versions. I am pretty 1977 * convinced that this should be abstracted out and bounced up a level 1978 */ 1979 1980enum nlm4_stats 1981do_lock(struct file_lock *fl) 1982{ 1983 enum partialfilelock_status pfsret; 1984 enum nlm4_stats retval; 1985 1986 debuglog("Entering do_lock...\n"); 1987 1988 pfsret = lock_partialfilelock(fl); 1989 1990 switch (pfsret) { 1991 case PFL_GRANTED: 1992 debuglog("PFL lock granted"); 1993 dump_filelock(fl); 1994 retval = (fl->flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 1995 break; 1996 case PFL_GRANTED_DUPLICATE: 1997 debuglog("PFL lock granted--duplicate id detected"); 1998 dump_filelock(fl); 1999 retval = (fl->flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 2000 break; 2001 case PFL_NFSDENIED: 2002 case PFL_HWDENIED: 2003 debuglog("PFL_NFS lock denied"); 2004 dump_filelock(fl); 2005 retval = (fl->flags & LOCK_V4) ? nlm4_denied : (nlm4_stats) nlm_denied; 2006 break; 2007 case PFL_NFSBLOCKED: 2008 case PFL_HWBLOCKED: 2009 debuglog("PFL_NFS blocking lock denied. Queued.\n"); 2010 dump_filelock(fl); 2011 retval = (fl->flags & LOCK_V4) ? nlm4_blocked : (nlm4_stats) nlm_blocked; 2012 break; 2013 case PFL_NFSRESERR: 2014 case PFL_HWRESERR: 2015 case PFL_NFSDENIED_NOLOCK: 2016 case PFL_HWDENIED_NOLOCK: 2017 debuglog("PFL lock resource alocation fail\n"); 2018 dump_filelock(fl); 2019 retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2020 break; 2021 case PFL_HWDENIED_STALEFH: 2022 debuglog("PFL_NFS lock denied STALEFH"); 2023 dump_filelock(fl); 2024 retval = (fl->flags & LOCK_V4) ? nlm4_stale_fh : (nlm4_stats) nlm_denied; 2025 break; 2026 case PFL_HWDENIED_READONLY: 2027 debuglog("PFL_NFS lock denied READONLY"); 2028 dump_filelock(fl); 2029 retval = (fl->flags & LOCK_V4) ? nlm4_rofs : (nlm4_stats) nlm_denied; 2030 break; 2031 default: 2032 debuglog("PFL lock *FAILED*"); 2033 dump_filelock(fl); 2034 retval = (fl->flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 2035 break; 2036 } 2037 2038 debuglog("Exiting do_lock...\n"); 2039 2040 return retval; 2041} 2042 2043enum nlm4_stats 2044do_unlock(struct file_lock *fl) 2045{ 2046 enum partialfilelock_status pfsret; 2047 enum nlm4_stats retval; 2048 2049 debuglog("Entering do_unlock...\n"); 2050 pfsret = unlock_partialfilelock(fl, 0); 2051 2052 switch (pfsret) { 2053 case PFL_GRANTED: 2054 debuglog("PFL unlock granted"); 2055 dump_filelock(fl); 2056 retval = (fl->flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 2057 break; 2058 case PFL_NFSDENIED: 2059 case PFL_HWDENIED: 2060 debuglog("PFL_NFS unlock denied"); 2061 dump_filelock(fl); 2062 retval = (fl->flags & LOCK_V4) ? nlm4_denied : (nlm4_stats) nlm_denied; 2063 break; 2064 case PFL_NFSDENIED_NOLOCK: 2065 case PFL_HWDENIED_NOLOCK: 2066 debuglog("PFL_NFS no lock found\n"); 2067 retval = (fl->flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 2068 break; 2069 case PFL_NFSRESERR: 2070 case PFL_HWRESERR: 2071 debuglog("PFL unlock resource failure"); 2072 dump_filelock(fl); 2073 retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2074 break; 2075 default: 2076 debuglog("PFL unlock *FAILED*"); 2077 dump_filelock(fl); 2078 retval = (fl->flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 2079 break; 2080 } 2081 2082 debuglog("Exiting do_unlock...\n"); 2083 2084 return retval; 2085} 2086 2087/* 2088 * do_clear 2089 * 2090 * This routine is non-existent because it doesn't have a return code. 2091 * It is here for completeness in case someone *does* need to do return 2092 * codes later. A decent compiler should optimize this away. 2093 */ 2094 2095void 2096do_clear(const char *hostname) 2097{ 2098 2099 clear_partialfilelock(hostname); 2100} 2101 2102/* 2103 * do_notify_mounts() 2104 * 2105 * Notify any NFS mounts to recover the locks they held on the given server. 2106 */ 2107void 2108do_notify_mounts(const char *hostname) 2109{ 2110 struct addrinfo *ailist = NULL, *ai, aihints; 2111 struct lockd_notify ln, *lnp = NULL; 2112 int addrcount, addrmax; 2113 2114 /* Get the list of addresses for this hostname. */ 2115 /* Send a lockd-notify with the addresses into the kernel. */ 2116 debuglog("do_notify_mounts for %s", hostname); 2117 bzero(&aihints, sizeof(aihints)); 2118 aihints.ai_socktype = SOCK_STREAM; 2119 if (getaddrinfo(hostname, NULL, &aihints, &ailist)) 2120 return; 2121 for (addrcount=0, ai = ailist; ai; ai = ai->ai_next) { 2122 if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6)) 2123 continue; 2124 addrcount++; 2125 } 2126 debuglog("do_notify_mounts for %s, addrcount %d", hostname, addrcount); 2127 if (addrcount > 1) 2128 lnp = malloc(sizeof(*lnp) + ((addrcount-1) * sizeof(struct sockaddr_storage))); 2129 if (lnp) { /* send all the addresses in one notification */ 2130 addrmax = addrcount; 2131 } else { /* just send single-address notification(s) */ 2132 lnp = &ln; 2133 addrmax = 1; 2134 } 2135 2136 ai = ailist; 2137 while (ai && (addrcount > 0)) { 2138 lnp->ln_version = LOCKD_NOTIFY_VERSION; 2139 lnp->ln_flags = 0; 2140 lnp->ln_addrcount = 0; 2141 for (; ai && (lnp->ln_addrcount < addrmax); ai = ai->ai_next) { 2142 if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6)) 2143 continue; 2144 if (config.verbose > 1) { 2145 char addrbuf[NI_MAXHOST], *addr = NULL; 2146 void *sinaddr = (ai->ai_family == AF_INET) ? 2147 (void*)&((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr : 2148 (void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr; 2149 if (inet_ntop(ai->ai_family, sinaddr, addrbuf, sizeof(addrbuf))) 2150 addr = addrbuf; 2151 debuglog("do_notify_mounts for %s, addr %d ai fam %d len %d %s", 2152 hostname, lnp->ln_addrcount, ai->ai_family, ai->ai_addrlen, 2153 addr ? addr : "???"); 2154 } 2155 bcopy(ai->ai_addr, &lnp->ln_addr[lnp->ln_addrcount], 2156 MIN(sizeof(lnp->ln_addr[lnp->ln_addrcount]), ai->ai_addr->sa_len)); 2157 lnp->ln_addrcount++; 2158 addrcount--; 2159 } 2160 nfsclnt(NFSCLNT_LOCKDNOTIFY, lnp); 2161 } 2162 freeaddrinfo(ailist); 2163} 2164 2165/* 2166 * The following routines are all called from the code which the 2167 * RPC layer invokes 2168 */ 2169 2170/* 2171 * testlock(): inform the caller if the requested lock would be granted 2172 * 2173 * returns NULL if lock would granted 2174 * returns pointer to a conflicting nlm4_holder if not 2175 */ 2176 2177struct nlm4_holder * 2178testlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused) 2179{ 2180 struct file_lock test_fl, *conflicting_fl; 2181 2182 if (lock->fh.n_len > NFSV3_MAX_FH_SIZE) { 2183 debuglog("received fhandle size %d, max size %d", 2184 lock->fh.n_len, NFSV3_MAX_FH_SIZE); 2185 return NULL; 2186 } 2187 2188 bzero(&test_fl, sizeof(test_fl)); 2189 2190 test_fl.filehandle.n_len = lock->fh.n_len; 2191 test_fl.filehandle.n_bytes = lock->fh.n_bytes; 2192 copy_nlm4_lock_to_nlm4_holder(lock, exclusive, &test_fl.client); 2193 2194 siglock(); 2195 do_test(&test_fl, &conflicting_fl); 2196 2197 if (conflicting_fl == NULL) { 2198 debuglog("No conflicting lock found\n"); 2199 sigunlock(); 2200 return NULL; 2201 } else { 2202 debuglog("Found conflicting lock\n"); 2203 dump_filelock(conflicting_fl); 2204 sigunlock(); 2205 return (&conflicting_fl->client); 2206 } 2207} 2208 2209/* 2210 * getlock: try to aquire the lock. 2211 * If file is already locked and we can sleep, put the lock in the list with 2212 * status LKST_WAITING; it'll be processed later. 2213 * Otherwise try to lock. If we're allowed to block, fork a child which 2214 * will do the blocking lock. 2215 */ 2216 2217enum nlm4_stats 2218getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags) 2219{ 2220 struct file_lock *newfl; 2221 enum nlm4_stats retval; 2222 2223 debuglog("Entering getlock...\n"); 2224 2225 if (grace_expired == 0 && lckarg->reclaim == 0) 2226 return (flags & LOCK_V4) ? 2227 nlm4_denied_grace_period : (nlm4_stats) nlm_denied_grace_period; 2228 2229 if (lckarg->alock.fh.n_len > NFSV3_MAX_FH_SIZE) { 2230 debuglog("received fhandle size %d, max size %d", 2231 lckarg->alock.fh.n_len, NFSV3_MAX_FH_SIZE); 2232 return (flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 2233 } 2234 2235 /* allocate new file_lock for this request */ 2236 newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->alock.fh, 2237 svc_getcaller_sa(rqstp->rq_xprt), 2238 lckarg->alock.caller_name); 2239 if (newfl == NULL) { 2240 syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno)); 2241 /* failed */ 2242 return (flags & LOCK_V4) ? 2243 nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2244 } 2245 2246 fill_file_lock(newfl, 2247 lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset, 2248 lckarg->alock.l_len, 2249 lckarg->state, 0, flags, lckarg->block); 2250 2251 /* 2252 * newfl is now fully constructed and deallocate_file_lock 2253 * can now be used to delete it 2254 */ 2255 2256 siglock(); 2257 debuglog("Pointer to new lock is %p\n",newfl); 2258 2259 retval = do_lock(newfl); 2260 2261 debuglog("Pointer to new lock is %p\n",newfl); 2262 sigunlock(); 2263 2264 switch (retval) 2265 { 2266 case nlm4_granted: 2267 /* case nlm_granted: is the same as nlm4_granted */ 2268 /* do_mon(lckarg->alock.caller_name); */ 2269 break; 2270 case nlm4_blocked: 2271 /* case nlm_blocked: is the same as nlm4_blocked */ 2272 /* do_mon(lckarg->alock.caller_name); */ 2273 break; 2274 default: 2275 deallocate_file_lock(newfl); 2276 break; 2277 } 2278 2279 debuglog("Exiting getlock...\n"); 2280 2281 return retval; 2282} 2283 2284 2285/* unlock a filehandle */ 2286enum nlm4_stats 2287unlock(nlm4_lock *lock, const int flags) 2288{ 2289 struct file_lock fl; 2290 enum nlm4_stats err; 2291 2292 debuglog("Entering unlock...\n"); 2293 2294 if (lock->fh.n_len > NFSV3_MAX_FH_SIZE) { 2295 debuglog("received fhandle size %d, max size %d", 2296 lock->fh.n_len, NFSV3_MAX_FH_SIZE); 2297 return (flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 2298 } 2299 2300 siglock(); 2301 2302 bzero(&fl,sizeof(struct file_lock)); 2303 fl.filehandle.n_len = lock->fh.n_len; 2304 fl.filehandle.n_bytes = lock->fh.n_bytes; 2305 2306 copy_nlm4_lock_to_nlm4_holder(lock, 0, &fl.client); 2307 2308 err = do_unlock(&fl); 2309 2310 sigunlock(); 2311 2312 debuglog("Exiting unlock...\n"); 2313 2314 return err; 2315} 2316 2317/* cancel a blocked lock request */ 2318enum nlm4_stats 2319cancellock(nlm4_cancargs *args, const int flags) 2320{ 2321 struct file_lock *ifl, *nfl; 2322 enum nlm4_stats err; 2323 2324 debuglog("Entering cancellock...\n"); 2325 2326 if (args->alock.fh.n_len > NFSV3_MAX_FH_SIZE) { 2327 debuglog("received fhandle size %d, max size %d", 2328 args->alock.fh.n_len, NFSV3_MAX_FH_SIZE); 2329 return (flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 2330 } 2331 2332 siglock(); 2333 2334 err = (flags & LOCK_V4) ? nlm4_denied : (nlm4_stats) nlm_denied; 2335 2336 /* 2337 * scan blocked lock list for matching request and remove/destroy 2338 */ 2339 ifl = LIST_FIRST(&blockedlocklist_head); 2340 for ( ; ifl != NULL; ifl = nfl) { 2341 nfl = LIST_NEXT(ifl, nfslocklist); 2342 2343 /* compare lock fh - filehandle */ 2344 if (!same_netobj(&args->alock.fh, &ifl->filehandle)) 2345 continue; 2346 2347 /* compare lock caller_name - client_name */ 2348 if (strncmp(args->alock.caller_name, ifl->client_name, SM_MAXSTRLEN)) 2349 continue; 2350 2351 /* Note: done't compare cookie - client_cookie */ 2352 /* The cookie may be specific to the cancel request */ 2353 /* and not be the same as the one in the original lock request. */ 2354 2355 /* compare lock oh - client.oh */ 2356 if (!same_netobj(&args->alock.oh, &ifl->client.oh)) 2357 continue; 2358 2359 /* compare lock svid - client.svid */ 2360 if (args->alock.svid != ifl->client.svid) 2361 continue; 2362 2363 /* compare lock l_offset - client.l_offset */ 2364 if (args->alock.l_offset != ifl->client.l_offset) 2365 continue; 2366 2367 /* compare lock l_len - client.l_len */ 2368 if (args->alock.l_len != ifl->client.l_len) 2369 continue; 2370 2371 /* compare exclusive - client.exclusive */ 2372 if (args->exclusive != ifl->client.exclusive) 2373 continue; 2374 2375 /* got it */ 2376 remove_blockingfilelock(ifl); 2377 deallocate_file_lock(ifl); 2378 err = (flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 2379 break; 2380 } 2381 2382 sigunlock(); 2383 2384 debuglog("Exiting cancellock...\n"); 2385 2386 return err; 2387} 2388 2389 2390/* 2391 * XXX: The following monitor/unmonitor routines 2392 * have not been extensively tested (ie. no regression 2393 * script exists like for the locking sections) 2394 */ 2395 2396/* 2397 * Find a lock host on a queue. If found: 2398 * bump the ref, 2399 * bump the access time, 2400 * dequeue it from the queue it was found on, 2401 * enqueue it at the front of the "in use" queue. 2402 */ 2403static struct host * 2404get_lock_host(struct hostlst_head *hd, const char *hostname, const struct sockaddr *saddr) 2405{ 2406 struct host *ihp; 2407 2408 if (!hostname && !saddr) 2409 return (NULL); 2410 2411 debuglog("get_lock_host %s\n", hostname ? hostname : "addr"); 2412 TAILQ_FOREACH(ihp, hd, hostlst) { 2413 if (hostname && (strncmp(hostname, ihp->name, SM_MAXSTRLEN) != 0)) 2414 continue; 2415 if (saddr && addrcmp(saddr, (struct sockaddr*)&ihp->addr)) 2416 continue; 2417 TAILQ_REMOVE(hd, ihp, hostlst); 2418 /* 2419 * Host is already monitored, so just bump the 2420 * reference count. But don't bump the reference 2421 * count if we're adding additional client-side 2422 * references. Client-side monitors are done by 2423 * address, are never unmonitored, and should only 2424 * take one refcount. Otherwise, repeated calls 2425 * could cause the refcount to wrap. 2426 */ 2427 if (!saddr || !ihp->addr.ss_len) 2428 ++ihp->refcnt; 2429 ihp->lastuse = currsec; 2430 /* Host should only be in the monitor list once */ 2431 TAILQ_INSERT_HEAD(&hostlst_head, ihp, hostlst); 2432 break; 2433 } 2434 debuglog("get_lock_host %s %s\n", 2435 ihp == NULL ? "did not find" : "found", hostname ? hostname : "addr"); 2436 return (ihp); 2437} 2438 2439/* 2440 * monitor_lock_host: monitor lock hosts locally with a ref count and 2441 * inform statd 2442 */ 2443void 2444monitor_lock_host_by_name(const char *hostname, const struct sockaddr *saddr) 2445{ 2446 struct host *ihp; 2447 2448 debuglog("monitor_lock_host: %s\n", hostname); 2449 ihp = get_lock_host(&hostlst_head, hostname, NULL); 2450 if (ihp == NULL) 2451 ihp = get_lock_host(&hostlst_unref, hostname, NULL); 2452 if (ihp != NULL) { 2453 if (ihp->revname) 2454 debuglog("Monitor_lock_host: %s - %s (cached)\n", 2455 ihp->name, ihp->revname); 2456 else 2457 debuglog("Monitor_lock_host: %s (cached)\n", ihp->name); 2458 return; 2459 } 2460 2461 monitor_lock_host(hostname, saddr); 2462} 2463 2464void 2465monitor_lock_host_by_addr(const struct sockaddr *saddr) 2466{ 2467 struct host *ihp; 2468 char hostaddr[NI_MAXHOST]; 2469 char hostname[NI_MAXHOST]; 2470 2471 if (getnameinfo(saddr, saddr->sa_len, hostaddr, sizeof(hostaddr), 2472 NULL, 0, NI_NUMERICHOST)) { 2473 debuglog("monitor_lock_host: bad address\n"); 2474 return; 2475 } 2476 debuglog("monitor_lock_host: %s\n", hostaddr); 2477 ihp = get_lock_host(&hostlst_head, NULL, saddr); 2478 if (ihp == NULL) 2479 ihp = get_lock_host(&hostlst_unref, NULL, saddr); 2480 if (ihp != NULL) { 2481 debuglog("Monitor_lock_host: %s (cached)\n", ihp->name); 2482 return; 2483 } 2484 2485 if (!getnameinfo(saddr, saddr->sa_len, hostname, sizeof(hostname), NULL, 0, 0)) 2486 monitor_lock_host(hostname, saddr); 2487 else 2488 monitor_lock_host(hostaddr, saddr); 2489} 2490 2491static void 2492monitor_lock_host(const char *hostname, const struct sockaddr *saddr) 2493{ 2494 struct host *nhp; 2495 struct mon smon; 2496 struct sm_stat_res sres; 2497 int rpcret; 2498 int retrying = 0; 2499 size_t n; 2500 char hostbuf[NI_MAXHOST]; 2501 2502 /* Host is not yet monitored, add it */ 2503 debuglog("Monitor_lock_host: %s (creating)\n", hostname); 2504 n = strnlen(hostname, SM_MAXSTRLEN); 2505 if (n == SM_MAXSTRLEN) { 2506 debuglog("monitor_lock_host: hostname too long\n"); 2507 return; 2508 } 2509 nhp = (struct host *) malloc(sizeof(struct host)); 2510 if (nhp == NULL) { 2511 debuglog("Unable to allocate entry for statd mon\n"); 2512 return; 2513 } 2514 2515 /* Allocated new host entry, now fill the fields */ 2516 nhp->name = strdup(hostname); 2517 if (nhp->name == NULL) { 2518 debuglog("Unable to allocate entry name for statd mon\n"); 2519 free(nhp); 2520 return; 2521 } 2522 nhp->revname = NULL; 2523 nhp->refcnt = 1; 2524 nhp->lastuse = currsec; 2525 if (saddr) { 2526 bcopy(saddr, &nhp->addr, saddr->sa_len); 2527 } else { 2528 nhp->addr.ss_len = 0; 2529 } 2530 debuglog("Locally Monitoring host '%s'\n", hostname); 2531 2532 debuglog("Attempting to tell statd\n"); 2533 2534 bzero(&smon,sizeof(smon)); 2535 2536 smon.mon_id.mon_name = nhp->name; 2537 smon.mon_id.my_id.my_name = (char *) "localhost\0"; 2538 2539 smon.mon_id.my_id.my_prog = NLM_PROG; 2540 smon.mon_id.my_id.my_vers = NLM_SM; 2541 smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; 2542 2543retry: 2544 rpcret = callrpc((char *) "localhost", SM_PROG, SM_VERS, SM_MON, (xdrproc_t)xdr_mon, 2545 &smon, (xdrproc_t)xdr_sm_stat_res, &sres); 2546 2547 if (rpcret == 0) { 2548 if (sres.res_stat == stat_fail && !retrying) { 2549 debuglog("Statd call failed\n"); 2550 /* 2551 * It's possible that the hostname provided 2552 * by the client isn't valid. Retry with a 2553 * a hostname reverse-mapped from the client's 2554 * address (if provided) and stash this name 2555 * in the host entry so that we can identify it 2556 * with this name later when we ask statd to 2557 * unmonitor it. 2558 */ 2559 if (saddr && 2560 !getnameinfo(saddr, saddr->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, 0) && 2561 strcmp(nhp->name, hostbuf)) { 2562 debuglog("Statd retry with '%s'\n", hostbuf); 2563 smon.mon_id.mon_name = hostbuf; 2564 nhp->revname = strdup(hostbuf); 2565 if (nhp->revname == NULL) { 2566 debuglog("No memory for revname\n"); 2567 free(nhp->name); 2568 free(nhp); 2569 return; 2570 } 2571 retrying = 1; 2572 goto retry; 2573 } 2574 } 2575 } else { 2576 debuglog("Rpc call to statd failed with return value: %d\n", 2577 rpcret); 2578 } 2579 2580 /* 2581 * Insert host in the monitor list, even if statd 2582 * doesn't like it. If the name is unacceptable 2583 * to statd, at least we'll avoid subsequent rejection 2584 * on every lock request. 2585 */ 2586 TAILQ_INSERT_HEAD(&hostlst_head, nhp, hostlst); 2587} 2588 2589/* 2590 * unmonitor_lock_host: clear monitor ref counts and inform statd when gone 2591 */ 2592void 2593unmonitor_lock_host(const char *hostname) 2594{ 2595 struct host *ihp; 2596 2597 TAILQ_FOREACH(ihp, &hostlst_head, hostlst) { 2598 if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) { 2599 /* Host is unmonitored, drop refcount */ 2600 --ihp->refcnt; 2601 /* Host should only be in the monitor list once */ 2602 break; 2603 } 2604 } 2605 2606 if (ihp == NULL) { 2607 debuglog("Could not find host %16s in mon list\n", hostname); 2608 return; 2609 } 2610 2611 if (ihp->refcnt > 0) 2612 return; 2613 2614 if (ihp->refcnt < 0) { 2615 debuglog("Negative refcount!: %d\n", ihp->refcnt); 2616 } 2617 2618 TAILQ_REMOVE(&hostlst_head, ihp, hostlst); 2619 TAILQ_INSERT_HEAD(&hostlst_unref, ihp, hostlst); 2620 if (config.host_monitor_cache_timeout <= 0) 2621 destroy_lock_host(ihp); 2622} 2623 2624void 2625destroy_lock_host(struct host *ihp) 2626{ 2627 struct mon_id smon_id; 2628 struct sm_stat smstat; 2629 int rpcret; 2630 char *name; 2631 2632 /* 2633 * If the client was monitored with a hostname obtained from 2634 * its address, then use that instead of the possibly invalid 2635 * hostname provided in caller_name. 2636 */ 2637 name = ihp->revname ? ihp->revname : ihp->name; 2638 debuglog("Attempting to unmonitor host %16s\n", name); 2639 2640 bzero(&smon_id,sizeof(smon_id)); 2641 smon_id.mon_name = name; 2642 smon_id.my_id.my_name = (char *) "localhost"; 2643 smon_id.my_id.my_prog = NLM_PROG; 2644 smon_id.my_id.my_vers = NLM_SM; 2645 smon_id.my_id.my_proc = NLM_SM_NOTIFY; 2646 2647 rpcret = callrpc((char *) "localhost", SM_PROG, SM_VERS, SM_UNMON, (xdrproc_t)xdr_mon_id, 2648 &smon_id, (xdrproc_t)xdr_sm_stat, &smstat); 2649 2650 if (rpcret != 0) { 2651 debuglog("Rpc call to unmonitor statd failed with " 2652 " return value: %d: %s", rpcret, clnt_sperrno(rpcret)); 2653 } else { 2654 debuglog("Succeeded unmonitoring %16s\n", ihp->name); 2655 } 2656 2657 TAILQ_REMOVE(&hostlst_unref, ihp, hostlst); 2658 if (ihp->name) 2659 free(ihp->name); 2660 if (ihp->revname) 2661 free(ihp->revname); 2662 free(ihp); 2663} 2664 2665/* 2666 * returns 1 if there are hosts to expire or 0 if there are none. 2667 */ 2668int 2669expire_lock_hosts(void) 2670{ 2671 struct host *ihp; 2672 2673 for ( ;; ) { 2674 ihp = TAILQ_LAST(&hostlst_unref, hostlst_head); 2675 if (ihp == NULL) 2676 break; 2677 if ((config.host_monitor_cache_timeout > 0) && 2678 (ihp->lastuse >= (currsec - config.host_monitor_cache_timeout))) 2679 break; 2680 debuglog("expire_lock_hosts: expiring %s %d %d %d\n", 2681 ihp->name, (int)ihp->lastuse, 2682 (int)currsec, (int)currsec - config.host_monitor_cache_timeout); 2683 destroy_lock_host(ihp); 2684 } 2685 return (TAILQ_LAST(&hostlst_unref, hostlst_head) != NULL); 2686} 2687 2688/* 2689 * notify: Clear all locks from a host if statd complains 2690 * 2691 * XXX: This routine has not been thoroughly tested. However, neither 2692 * had the old one been. It used to compare the statd crash state counter 2693 * to the current lock state. The upshot of this was that it basically 2694 * cleared all locks from the specified host 99% of the time (with the 2695 * other 1% being a bug). Consequently, the assumption is that clearing 2696 * all locks from a host when notified by statd is acceptable. 2697 * 2698 * Please note that this routine skips the usual level of redirection 2699 * through a do_* type routine. This introduces a possible level of 2700 * error and might better be written as do_notify and take this one out. 2701 2702 */ 2703 2704void 2705notify(const char *hostname, const int state) 2706{ 2707 debuglog("notify from %s, new state %d", hostname, state); 2708 2709 siglock(); 2710 do_clear(hostname); 2711 do_notify_mounts(hostname); 2712 sigunlock(); 2713 2714 debuglog("Leaving notify\n"); 2715} 2716 2717int 2718send_granted(fl, opcode) 2719 struct file_lock *fl; 2720 int opcode __unused; 2721{ 2722 CLIENT *cli; 2723 static char dummy; 2724 struct timeval timeo; 2725 enum clnt_stat rv; 2726 static struct nlm_res retval; 2727 static struct nlm4_res retval4; 2728 2729 debuglog("About to send granted on blocked lock\n"); 2730 2731 cli = get_client(fl->addr, (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS, 0, 0); 2732 if (cli == NULL) { 2733 syslog(LOG_NOTICE, "failed to get CLIENT for %s", 2734 fl->client_name); 2735 /* 2736 * We fail to notify remote that the lock has been granted. 2737 * The client will timeout and retry, the lock will be 2738 * granted at this time. 2739 */ 2740 return -1; 2741 } 2742 timeo.tv_sec = 0; 2743 timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ 2744 2745 fl->granted_cookie = ++send_granted_cookie; 2746 if (!send_granted_cookie) 2747 send_granted_cookie++; 2748 2749 if (fl->flags & LOCK_V4) { 2750 static nlm4_testargs res; 2751 res.cookie.n_len = sizeof(fl->granted_cookie); 2752 res.cookie.n_bytes = (uint8_t*)&fl->granted_cookie; 2753 res.exclusive = fl->client.exclusive; 2754 res.alock.caller_name = fl->client_name; 2755 res.alock.fh.n_len = fl->filehandle.n_len; 2756 res.alock.fh.n_bytes = fl->filehandle.n_bytes; 2757 res.alock.oh = fl->client.oh; 2758 res.alock.svid = fl->client.svid; 2759 res.alock.l_offset = fl->client.l_offset; 2760 res.alock.l_len = fl->client.l_len; 2761 debuglog("sending v4 reply%s", 2762 (fl->flags & LOCK_ASYNC) ? " (async)":""); 2763 if (fl->flags & LOCK_ASYNC) { 2764 rv = clnt_call(cli, NLM4_GRANTED_MSG, 2765 (xdrproc_t)xdr_nlm4_testargs, &res, (xdrproc_t)xdr_void, &dummy, timeo); 2766 } else { 2767 rv = clnt_call(cli, NLM4_GRANTED, 2768 (xdrproc_t)xdr_nlm4_testargs, &res, (xdrproc_t)xdr_nlm4_res, 2769 &retval4, timeo); 2770 } 2771 } else { 2772 static nlm_testargs res; 2773 2774 res.cookie.n_len = sizeof(fl->granted_cookie); 2775 res.cookie.n_bytes = (uint8_t*)&fl->granted_cookie; 2776 res.exclusive = fl->client.exclusive; 2777 res.alock.caller_name = fl->client_name; 2778 res.alock.fh.n_len = fl->filehandle.n_len; 2779 res.alock.fh.n_bytes = fl->filehandle.n_bytes; 2780 res.alock.oh = fl->client.oh; 2781 res.alock.svid = fl->client.svid; 2782 res.alock.l_offset = fl->client.l_offset; 2783 res.alock.l_len = fl->client.l_len; 2784 debuglog("sending v1 reply%s", 2785 (fl->flags & LOCK_ASYNC) ? " (async)":""); 2786 if (fl->flags & LOCK_ASYNC) { 2787 rv = clnt_call(cli, NLM_GRANTED_MSG, 2788 (xdrproc_t)xdr_nlm_testargs, &res, (xdrproc_t)xdr_void, &dummy, timeo); 2789 } else { 2790 rv = clnt_call(cli, NLM_GRANTED, 2791 (xdrproc_t)xdr_nlm_testargs, &res, (xdrproc_t)xdr_nlm_res, 2792 &retval, timeo); 2793 } 2794 } 2795 if (config.verbose > 2) 2796 debuglog("clnt_call returns %d(%s) for granted", 2797 rv, clnt_sperrno(rv)); 2798 2799 if ((rv != RPC_SUCCESS) && 2800 !((fl->flags & LOCK_ASYNC) && (rv == RPC_TIMEDOUT))) 2801 return -1; 2802 return 0; 2803} 2804 2805/* 2806 * granted_failed: remove a granted lock that wasn't successfully 2807 * accepted by the client 2808 */ 2809void 2810granted_failed(nlm4_res *arg) 2811{ 2812 u_int64_t cookie; 2813 struct file_lock *ifl; 2814 2815 debuglog("Entering granted_failed, status %d\n", arg->stat.stat); 2816 2817 if (arg->cookie.n_len != sizeof(cookie)) { 2818 debuglog("Exiting granted_failed: bogus cookie size %d\n", 2819 arg->cookie.n_len); 2820 return; 2821 } 2822 bcopy(arg->cookie.n_bytes, &cookie, sizeof(cookie)); 2823 debuglog("granted_failed, cookie 0x%llx\n", cookie); 2824 2825 LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 2826 debuglog("Pointer to file lock: %p\n",ifl); 2827 2828 debuglog("****Dump of ifl****\n"); 2829 dump_filelock(ifl); 2830 2831 if (ifl->granted_cookie != cookie) 2832 continue; 2833 2834 debuglog("granted_failed: cookie found\n"); 2835 break; 2836 } 2837 2838 if (ifl) { 2839 do_unlock(ifl); 2840 /* ifl is NO LONGER VALID AT THIS POINT */ 2841 } else { 2842 debuglog("granted_failed: cookie NOT FOUND\n"); 2843 } 2844 2845 debuglog("Exiting granted_failed\n"); 2846} 2847 2848/* 2849 * getshare: try to acquire a share reservation 2850 */ 2851enum nlm4_stats 2852getshare(nlm_shareargs *shrarg, struct svc_req *rqstp __unused, const int flags) 2853{ 2854 struct sharefile *shrfile; 2855 struct file_share *sh; 2856 size_t n; 2857 2858 debuglog("Entering getshare...\n"); 2859 2860 if (grace_expired == 0 && shrarg->reclaim == 0) { 2861 debuglog("getshare denied - grace period\n"); 2862 return (flags & LOCK_V4) ? 2863 nlm4_denied_grace_period : 2864 (nlm4_stats) nlm_denied_grace_period; 2865 } 2866 2867 if (shrarg->share.fh.n_len > NFSV3_MAX_FH_SIZE) { 2868 debuglog("received fhandle size %d, max size %d", 2869 shrarg->share.fh.n_len, NFSV3_MAX_FH_SIZE); 2870 return (flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 2871 } 2872 2873 /* find file in list of share files */ 2874 LIST_FOREACH(shrfile, &nfssharefilelist_head, sharefilelist) { 2875 if ((shrarg->share.fh.n_len == shrfile->filehandle.n_len) && 2876 (bcmp(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes, 2877 shrarg->share.fh.n_len) == 0)) { 2878 /* shrfile is the correct file */ 2879 break; 2880 } 2881 } 2882 2883 /* if share file not found, create a new share file */ 2884 if (!shrfile) { 2885 fhandle_t fh; 2886 int fd; 2887 fh.fh_len = shrarg->share.fh.n_len; 2888 bcopy(shrarg->share.fh.n_bytes, fh.fh_data, fh.fh_len); 2889 fd = fhopen(&fh, O_RDONLY); 2890 if (fd < 0) { 2891 debuglog("fhopen failed (from %16s): %32s\n", 2892 shrarg->share.caller_name, strerror(errno)); 2893 if ((flags & LOCK_V4) == 0) 2894 return (nlm4_stats) nlm_denied; 2895 switch (errno) { 2896 case ESTALE: 2897 return nlm4_stale_fh; 2898 default: 2899 return nlm4_failed; 2900 } 2901 } 2902 shrfile = malloc(sizeof(struct sharefile)); 2903 if (!shrfile) { 2904 debuglog("getshare failed: can't allocate sharefile\n"); 2905 close(fd); 2906 return (flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2907 } 2908 shrfile->filehandle.n_len = shrarg->share.fh.n_len; 2909 shrfile->filehandle.n_bytes = malloc(shrarg->share.fh.n_len); 2910 if (!shrfile->filehandle.n_bytes) { 2911 debuglog("getshare failed: can't allocate sharefile filehandle\n"); 2912 free(shrfile); 2913 close(fd); 2914 return (flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2915 } 2916 bcopy(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes, 2917 shrarg->share.fh.n_len); 2918 shrfile->fd = fd; 2919 shrfile->refcount = 0; 2920 shrfile->sharelist_head.lh_first = NULL; 2921 LIST_INSERT_HEAD(&nfssharefilelist_head, shrfile, sharefilelist); 2922 } 2923 2924 /* compare request mode/access to current shares */ 2925 LIST_FOREACH(sh, &shrfile->sharelist_head, nfssharelist) { 2926 /* if request host/owner matches a current share... */ 2927 if ((strncmp(shrarg->share.caller_name, sh->client_name, SM_MAXSTRLEN) == 0) && 2928 same_netobj(&shrarg->share.oh, &sh->oh)) { 2929 /* ...then just update share mode/access */ 2930 sh->mode = shrarg->share.mode; 2931 sh->access = shrarg->share.access; 2932 debuglog("getshare: updated existing share\n"); 2933 return (flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 2934 } 2935 if (((shrarg->share.mode & sh->access) != 0) || 2936 ((shrarg->share.access & sh->mode) != 0)) { 2937 /* share request conflicts with existing share */ 2938 debuglog("getshare: conflicts with existing share\n"); 2939 return (flags & LOCK_V4) ? nlm4_denied : (nlm4_stats) nlm_denied; 2940 } 2941 } 2942 2943 /* create/init new share */ 2944 n = strnlen(shrarg->share.caller_name, SM_MAXSTRLEN); 2945 if (n < SM_MAXSTRLEN) { 2946 sh = malloc(sizeof(*sh) - sizeof(sh->client_name) + n + 1); 2947 } else { 2948 debuglog("getshare failed: hostname too long\n"); 2949 sh = NULL; 2950 } 2951 if (!sh) { 2952 debuglog("getshare failed: can't allocate share\n"); 2953 if (!shrfile->refcount) { 2954 LIST_REMOVE(shrfile, sharefilelist); 2955 close(shrfile->fd); 2956 free(shrfile->filehandle.n_bytes); 2957 free(shrfile); 2958 } 2959 return (flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2960 } 2961 bzero(sh, sizeof(*sh) - sizeof(sh->client_name)); 2962 sh->oh.n_len = shrarg->share.oh.n_len; 2963 sh->oh.n_bytes = malloc(sh->oh.n_len); 2964 if (!sh->oh.n_bytes) { 2965 debuglog("getshare failed: can't allocate share owner handle\n"); 2966 free(sh); 2967 if (!shrfile->refcount) { 2968 LIST_REMOVE(shrfile, sharefilelist); 2969 close(shrfile->fd); 2970 free(shrfile->filehandle.n_bytes); 2971 free(shrfile); 2972 } 2973 return (flags & LOCK_V4) ? nlm4_denied_nolocks : (nlm4_stats) nlm_denied_nolocks; 2974 } 2975 memcpy(sh->client_name, shrarg->share.caller_name, n); 2976 sh->client_name[n] = 0; 2977 sh->mode = shrarg->share.mode; 2978 sh->access = shrarg->share.access; 2979 2980 /* insert new share into file's share list */ 2981 LIST_INSERT_HEAD(&shrfile->sharelist_head, sh, nfssharelist); 2982 shrfile->refcount++; 2983 2984 debuglog("Exiting getshare...\n"); 2985 2986 return (flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 2987} 2988 2989 2990/* remove a share reservation */ 2991enum nlm4_stats 2992unshare(nlm_shareargs *shrarg, struct svc_req *rqstp __unused, const int flags) 2993{ 2994 struct sharefile *shrfile; 2995 struct file_share *sh; 2996 2997 debuglog("Entering unshare...\n"); 2998 2999 if (shrarg->share.fh.n_len > NFSV3_MAX_FH_SIZE) { 3000 debuglog("received fhandle size %d, max size %d", 3001 shrarg->share.fh.n_len, NFSV3_MAX_FH_SIZE); 3002 return (flags & LOCK_V4) ? nlm4_failed : (nlm4_stats) nlm_denied; 3003 } 3004 3005 /* find file in list of share files */ 3006 LIST_FOREACH(shrfile, &nfssharefilelist_head, sharefilelist) { 3007 if ((shrarg->share.fh.n_len == shrfile->filehandle.n_len) && 3008 (bcmp(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes, 3009 shrarg->share.fh.n_len) == 0)) { 3010 /* shrfile is the correct file */ 3011 break; 3012 } 3013 } 3014 3015 /* if share file not found, return success (per spec) */ 3016 if (!shrfile) { 3017 debuglog("unshare: no such share file\n"); 3018 return (flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 3019 } 3020 3021 /* find share */ 3022 LIST_FOREACH(sh, &shrfile->sharelist_head, nfssharelist) { 3023 /* if request host/owner matches a current share... */ 3024 if ((strncmp(shrarg->share.caller_name, sh->client_name, SM_MAXSTRLEN) == 0) && 3025 same_netobj(&shrarg->share.oh, &sh->oh)) 3026 break; 3027 } 3028 3029 /* if share not found, return success (per spec) */ 3030 if (!sh) { 3031 debuglog("unshare: no such share\n"); 3032 return (flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 3033 } 3034 3035 /* remove share from file and deallocate */ 3036 shrfile->refcount--; 3037 LIST_REMOVE(sh, nfssharelist); 3038 free(sh->oh.n_bytes); 3039 free(sh); 3040 3041 /* if file has no more shares, deallocate share file */ 3042 if (!shrfile->refcount) { 3043 debuglog("unshare: file has no more shares\n"); 3044 LIST_REMOVE(shrfile, sharefilelist); 3045 close(shrfile->fd); 3046 free(shrfile->filehandle.n_bytes); 3047 free(shrfile); 3048 } 3049 3050 debuglog("Exiting unshare...\n"); 3051 3052 return (flags & LOCK_V4) ? nlm4_granted : (nlm4_stats) nlm_granted; 3053} 3054 3055/* 3056 * do_free_all 3057 * 3058 * Wipe out all non-monitored locks and shares held by a host. 3059 */ 3060 3061void 3062do_free_all(const char *hostname) 3063{ 3064 struct file_lock *ifl, *nfl; 3065 struct sharefile *shrfile, *nshrfile; 3066 struct file_share *ifs, *nfs; 3067 enum partialfilelock_status pfsret; 3068 3069 /* clear non-monitored blocking file locks */ 3070 ifl = LIST_FIRST(&blockedlocklist_head); 3071 while (ifl != NULL) { 3072 nfl = LIST_NEXT(ifl, nfslocklist); 3073 3074 if (((ifl->flags & LOCK_MON) == 0) && 3075 (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0)) { 3076 remove_blockingfilelock(ifl); 3077 deallocate_file_lock(ifl); 3078 } 3079 3080 ifl = nfl; 3081 } 3082 3083 /* clear non-monitored file locks */ 3084restart: 3085 ifl = LIST_FIRST(&nfslocklist_head); 3086 while (ifl != NULL) { 3087 nfl = LIST_NEXT(ifl, nfslocklist); 3088 3089 if (((ifl->flags & LOCK_MON) == 0) && 3090 (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0)) { 3091 /* Unlock destroys ifl out from underneath */ 3092 pfsret = unlock_partialfilelock(ifl, 0); 3093 if (pfsret != PFL_GRANTED) { 3094 /* Uh oh... there was some sort of problem. */ 3095 /* If we restart the loop, we may get */ 3096 /* stuck here forever getting errors. */ 3097 /* So, let's just abort the whole scan. */ 3098 syslog(LOG_WARNING, "unmonitored lock clearing for %s failed: %d", 3099 hostname, pfsret); 3100 break; 3101 } 3102 /* ifl is NO LONGER VALID AT THIS POINT */ 3103 /* Note: the unlock may deallocate several existing locks. */ 3104 /* Therefore, we need to restart the scanning of the list, */ 3105 /* because nfl could be pointing to a freed lock. */ 3106 goto restart; 3107 } 3108 3109 ifl = nfl; 3110 } 3111 3112 /* clear shares */ 3113 shrfile = LIST_FIRST(&nfssharefilelist_head); 3114 while (shrfile != NULL) { 3115 nshrfile = LIST_NEXT(shrfile, sharefilelist); 3116 3117 ifs = LIST_FIRST(&shrfile->sharelist_head); 3118 while (ifs != NULL) { 3119 nfs = LIST_NEXT(ifs, nfssharelist); 3120 3121 if (strncmp(hostname, ifs->client_name, SM_MAXSTRLEN) == 0) { 3122 shrfile->refcount--; 3123 LIST_REMOVE(ifs, nfssharelist); 3124 free(ifs->oh.n_bytes); 3125 free(ifs); 3126 } 3127 3128 ifs = nfs; 3129 } 3130 3131 if (!shrfile->refcount) { 3132 LIST_REMOVE(shrfile, sharefilelist); 3133 close(shrfile->fd); 3134 free(shrfile->filehandle.n_bytes); 3135 free(shrfile); 3136 } 3137 3138 shrfile = nshrfile; 3139 } 3140 3141} 3142 3143 3144 3145/* 3146 * Routines below here have not been modified in the overhaul 3147 */ 3148 3149/* 3150 * Are these two routines still required since lockd is not spawning off 3151 * children to service locks anymore? Presumably they were originally 3152 * put in place to prevent a one child from changing the lock list out 3153 * from under another one. 3154 */ 3155 3156void 3157siglock(void) 3158{ 3159 sigset_t block; 3160 3161 sigemptyset(&block); 3162 sigaddset(&block, SIGCHLD); 3163 3164 if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { 3165 syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); 3166 } 3167} 3168 3169void 3170sigunlock(void) 3171{ 3172 sigset_t block; 3173 3174 sigemptyset(&block); 3175 sigaddset(&block, SIGCHLD); 3176 3177 if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { 3178 syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); 3179 } 3180} 3181 3182 3183