174462Salfred/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */ 274462Salfred 374462Salfred/* 487096Salfred * Copyright (c) 2001 Andrew P. Lentvorski, Jr. 574462Salfred * Copyright (c) 2000 Manuel Bouyer. 674462Salfred * 774462Salfred * Redistribution and use in source and binary forms, with or without 874462Salfred * modification, are permitted provided that the following conditions 974462Salfred * are met: 1074462Salfred * 1. Redistributions of source code must retain the above copyright 1174462Salfred * notice, this list of conditions and the following disclaimer. 1274462Salfred * 2. Redistributions in binary form must reproduce the above copyright 1374462Salfred * notice, this list of conditions and the following disclaimer in the 1474462Salfred * documentation and/or other materials provided with the distribution. 1574462Salfred * 3. All advertising materials mentioning features or use of this software 1674462Salfred * must display the following acknowledgement: 1774462Salfred * This product includes software developed by the University of 1874462Salfred * California, Berkeley and its contributors. 1974462Salfred * 4. Neither the name of the University nor the names of its contributors 2074462Salfred * may be used to endorse or promote products derived from this software 2174462Salfred * without specific prior written permission. 2274462Salfred * 2374462Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2474462Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2574462Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2674462Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2774462Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2874462Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2974462Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3074462Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3174462Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3274462Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3374462Salfred * SUCH DAMAGE. 3474462Salfred * 3574462Salfred */ 3674462Salfred 37146445Scharnier#include <sys/cdefs.h> 38146445Scharnier__FBSDID("$FreeBSD$"); 39146445Scharnier 4087096Salfred#define LOCKD_DEBUG 4187096Salfred 4274462Salfred#include <stdio.h> 4387096Salfred#ifdef LOCKD_DEBUG 4487096Salfred#include <stdarg.h> 4587096Salfred#endif 4674462Salfred#include <stdlib.h> 4774462Salfred#include <unistd.h> 4874462Salfred#include <fcntl.h> 4974462Salfred#include <syslog.h> 5074462Salfred#include <errno.h> 5174462Salfred#include <string.h> 5274462Salfred#include <signal.h> 5374462Salfred#include <rpc/rpc.h> 5474462Salfred#include <sys/types.h> 5574462Salfred#include <sys/stat.h> 5674462Salfred#include <sys/socket.h> 5774462Salfred#include <sys/param.h> 5874462Salfred#include <sys/mount.h> 5974462Salfred#include <sys/wait.h> 6074462Salfred#include <rpcsvc/sm_inter.h> 6174462Salfred#include <rpcsvc/nlm_prot.h> 6274462Salfred#include "lockd_lock.h" 6374462Salfred#include "lockd.h" 6474462Salfred 6587096Salfred#define MAXOBJECTSIZE 64 6687096Salfred#define MAXBUFFERSIZE 1024 6787096Salfred 6884923Salfred/* 6984923Salfred * A set of utilities for managing file locking 7084923Salfred * 7184923Salfred * XXX: All locks are in a linked list, a better structure should be used 72228990Suqs * to improve search/access efficiency. 7384923Salfred */ 7474462Salfred 7574462Salfred/* struct describing a lock */ 7674462Salfredstruct file_lock { 7787096Salfred LIST_ENTRY(file_lock) nfslocklist; 7874462Salfred fhandle_t filehandle; /* NFS filehandle */ 7974462Salfred struct sockaddr *addr; 8074462Salfred struct nlm4_holder client; /* lock holder */ 81165776Smjacob /* XXX: client_cookie used *only* in send_granted */ 8274462Salfred netobj client_cookie; /* cookie sent by the client */ 8374462Salfred int nsm_status; /* status from the remote lock manager */ 8474462Salfred int status; /* lock status, see below */ 8574462Salfred int flags; /* lock flags, see lockd_lock.h */ 8687096Salfred int blocking; /* blocking lock or not */ 87132254Smr char client_name[SM_MAXSTRLEN]; /* client_name is really variable 88132254Smr length and must be last! */ 8974462Salfred}; 9074462Salfred 9187096SalfredLIST_HEAD(nfslocklist_head, file_lock); 9287096Salfredstruct nfslocklist_head nfslocklist_head = LIST_HEAD_INITIALIZER(nfslocklist_head); 9387096Salfred 9487096SalfredLIST_HEAD(blockedlocklist_head, file_lock); 9587096Salfredstruct blockedlocklist_head blockedlocklist_head = LIST_HEAD_INITIALIZER(blockedlocklist_head); 9687096Salfred 9774462Salfred/* lock status */ 9874462Salfred#define LKST_LOCKED 1 /* lock is locked */ 9984923Salfred/* XXX: Is this flag file specific or lock specific? */ 10074462Salfred#define LKST_WAITING 2 /* file is already locked by another host */ 101228990Suqs#define LKST_PROCESSING 3 /* child is trying to acquire the lock */ 10274462Salfred#define LKST_DYING 4 /* must dies when we get news from the child */ 10374462Salfred 10487096Salfred/* struct describing a monitored host */ 10574462Salfredstruct host { 10674462Salfred LIST_ENTRY(host) hostlst; 10774462Salfred int refcnt; 108132254Smr char name[SM_MAXSTRLEN]; /* name is really variable length and 109132254Smr must be last! */ 11074462Salfred}; 11187096Salfred/* list of hosts we monitor */ 11287096SalfredLIST_HEAD(hostlst_head, host); 11387096Salfredstruct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head); 11474462Salfred 11587096Salfred/* 11687096Salfred * File monitoring handlers 11787096Salfred * XXX: These might be able to be removed when kevent support 11887096Salfred * is placed into the hardware lock/unlock routines. (ie. 11987096Salfred * let the kernel do all the file monitoring) 12087096Salfred */ 12174462Salfred 12287096Salfred/* Struct describing a monitored file */ 12387096Salfredstruct monfile { 12487096Salfred LIST_ENTRY(monfile) monfilelist; 12587096Salfred fhandle_t filehandle; /* Local access filehandle */ 12687096Salfred int fd; /* file descriptor: remains open until unlock! */ 12787096Salfred int refcount; 12887096Salfred int exclusive; 12987096Salfred}; 13087096Salfred 13187096Salfred/* List of files we monitor */ 13287096SalfredLIST_HEAD(monfilelist_head, monfile); 13387096Salfredstruct monfilelist_head monfilelist_head = LIST_HEAD_INITIALIZER(monfilelist_head); 13487096Salfred 13587096Salfredstatic int debugdelay = 0; 13687096Salfred 13787096Salfredenum nfslock_status { NFS_GRANTED = 0, NFS_GRANTED_DUPLICATE, 13887096Salfred NFS_DENIED, NFS_DENIED_NOLOCK, 13987096Salfred NFS_RESERR }; 14087096Salfred 14187096Salfredenum hwlock_status { HW_GRANTED = 0, HW_GRANTED_DUPLICATE, 142165776Smjacob HW_DENIED, HW_DENIED_NOLOCK, 14387096Salfred HW_STALEFH, HW_READONLY, HW_RESERR }; 14487096Salfred 14587096Salfredenum partialfilelock_status { PFL_GRANTED=0, PFL_GRANTED_DUPLICATE, PFL_DENIED, 146165776Smjacob PFL_NFSDENIED, PFL_NFSBLOCKED, PFL_NFSDENIED_NOLOCK, PFL_NFSRESERR, 14787096Salfred PFL_HWDENIED, PFL_HWBLOCKED, PFL_HWDENIED_NOLOCK, PFL_HWRESERR}; 14887096Salfred 14987096Salfredenum LFLAGS {LEDGE_LEFT, LEDGE_LBOUNDARY, LEDGE_INSIDE, LEDGE_RBOUNDARY, LEDGE_RIGHT}; 15087096Salfredenum RFLAGS {REDGE_LEFT, REDGE_LBOUNDARY, REDGE_INSIDE, REDGE_RBOUNDARY, REDGE_RIGHT}; 15187096Salfred/* XXX: WARNING! I HAVE OVERLOADED THIS STATUS ENUM! SPLIT IT APART INTO TWO */ 15287096Salfredenum split_status {SPL_DISJOINT=0, SPL_LOCK1=1, SPL_LOCK2=2, SPL_CONTAINED=4, SPL_RESERR=8}; 15387096Salfred 15487096Salfredenum partialfilelock_status lock_partialfilelock(struct file_lock *fl); 15587096Salfred 15687096Salfredvoid send_granted(struct file_lock *fl, int opcode); 15787096Salfredvoid siglock(void); 15887096Salfredvoid sigunlock(void); 15987096Salfredvoid monitor_lock_host(const char *hostname); 160121558Spetervoid unmonitor_lock_host(char *hostname); 16187096Salfred 16287096Salfredvoid copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src, 16387096Salfred const bool_t exclusive, struct nlm4_holder *dest); 16487199Salfredstruct file_lock * allocate_file_lock(const netobj *lockowner, 165132254Smr const netobj *matchcookie, 166132254Smr const struct sockaddr *addr, 167132254Smr const char *caller_name); 16887096Salfredvoid deallocate_file_lock(struct file_lock *fl); 16987199Salfredvoid fill_file_lock(struct file_lock *fl, const fhandle_t *fh, 170132254Smr const bool_t exclusive, const int32_t svid, 171132254Smr const u_int64_t offset, const u_int64_t len, 17287199Salfred const int state, const int status, const int flags, const int blocking); 17387096Salfredint regions_overlap(const u_int64_t start1, const u_int64_t len1, 174129302Sstefanf const u_int64_t start2, const u_int64_t len2); 17587096Salfredenum split_status region_compare(const u_int64_t starte, const u_int64_t lene, 17687096Salfred const u_int64_t startu, const u_int64_t lenu, 17787096Salfred u_int64_t *start1, u_int64_t *len1, u_int64_t *start2, u_int64_t *len2); 17887096Salfredint same_netobj(const netobj *n0, const netobj *n1); 17987096Salfredint same_filelock_identity(const struct file_lock *fl0, 18087096Salfred const struct file_lock *fl2); 18187096Salfred 18287096Salfredstatic void debuglog(char const *fmt, ...); 18387096Salfredvoid dump_static_object(const unsigned char* object, const int sizeof_object, 18487096Salfred unsigned char* hbuff, const int sizeof_hbuff, 18587096Salfred unsigned char* cbuff, const int sizeof_cbuff); 18687096Salfredvoid dump_netobj(const struct netobj *nobj); 18787096Salfredvoid dump_filelock(const struct file_lock *fl); 18887096Salfredstruct file_lock * get_lock_matching_unlock(const struct file_lock *fl); 18987096Salfredenum nfslock_status test_nfslock(const struct file_lock *fl, 19087096Salfred struct file_lock **conflicting_fl); 19187096Salfredenum nfslock_status lock_nfslock(struct file_lock *fl); 19287096Salfredenum nfslock_status delete_nfslock(struct file_lock *fl); 19387096Salfredenum nfslock_status unlock_nfslock(const struct file_lock *fl, 19487096Salfred struct file_lock **released_lock, struct file_lock **left_lock, 19587096Salfred struct file_lock **right_lock); 19687096Salfredenum hwlock_status lock_hwlock(struct file_lock *fl); 19787096Salfredenum split_status split_nfslock(const struct file_lock *exist_lock, 19887096Salfred const struct file_lock *unlock_lock, struct file_lock **left_lock, 19987096Salfred struct file_lock **right_lock); 200216603Suqsint duplicate_block(struct file_lock *fl); 20187096Salfredvoid add_blockingfilelock(struct file_lock *fl); 20287096Salfredenum hwlock_status unlock_hwlock(const struct file_lock *fl); 20387096Salfredenum hwlock_status test_hwlock(const struct file_lock *fl, 204165776Smjacob struct file_lock **conflicting_fl); 20587096Salfredvoid remove_blockingfilelock(struct file_lock *fl); 20687096Salfredvoid clear_blockingfilelock(const char *hostname); 20787096Salfredvoid retry_blockingfilelocklist(void); 20887096Salfredenum partialfilelock_status unlock_partialfilelock( 20987096Salfred const struct file_lock *fl); 21087096Salfredvoid clear_partialfilelock(const char *hostname); 21187096Salfredenum partialfilelock_status test_partialfilelock( 21287096Salfred const struct file_lock *fl, struct file_lock **conflicting_fl); 21387096Salfredenum nlm_stats do_test(struct file_lock *fl, 21487096Salfred struct file_lock **conflicting_fl); 21587096Salfredenum nlm_stats do_unlock(struct file_lock *fl); 21687096Salfredenum nlm_stats do_lock(struct file_lock *fl); 21787096Salfredvoid do_clear(const char *hostname); 218146445Scharniersize_t strnlen(const char *, size_t); 21987096Salfred 22087096Salfredvoid 22187096Salfreddebuglog(char const *fmt, ...) 22287096Salfred{ 22387096Salfred va_list ap; 22487096Salfred 22587096Salfred if (debug_level < 1) { 22687096Salfred return; 22787096Salfred } 22887096Salfred 22987096Salfred sleep(debugdelay); 23087096Salfred 23187096Salfred va_start(ap, fmt); 23287096Salfred vsyslog(LOG_DEBUG, fmt, ap); 23387096Salfred va_end(ap); 23487096Salfred} 23587096Salfred 23687096Salfredvoid 23787096Salfreddump_static_object(object, size_object, hbuff, size_hbuff, cbuff, size_cbuff) 23887096Salfred const unsigned char *object; 23987096Salfred const int size_object; 24087096Salfred unsigned char *hbuff; 24187096Salfred const int size_hbuff; 24287096Salfred unsigned char *cbuff; 24387096Salfred const int size_cbuff; 244165776Smjacob{ 24587096Salfred int i, objectsize; 24687096Salfred 24787096Salfred if (debug_level < 2) { 24887096Salfred return; 24987096Salfred } 25087096Salfred 25187096Salfred objectsize = size_object; 25287096Salfred 25387096Salfred if (objectsize == 0) { 25487096Salfred debuglog("object is size 0\n"); 25587096Salfred } else { 25687096Salfred if (objectsize > MAXOBJECTSIZE) { 25787096Salfred debuglog("Object of size %d being clamped" 25887096Salfred "to size %d\n", objectsize, MAXOBJECTSIZE); 25987096Salfred objectsize = MAXOBJECTSIZE; 26087096Salfred } 261165776Smjacob 26287096Salfred if (hbuff != NULL) { 26387096Salfred if (size_hbuff < objectsize*2+1) { 26487096Salfred debuglog("Hbuff not large enough." 26587096Salfred " Increase size\n"); 26687096Salfred } else { 26787096Salfred for(i=0;i<objectsize;i++) { 26887096Salfred sprintf(hbuff+i*2,"%02x",*(object+i)); 26987096Salfred } 27087096Salfred *(hbuff+i*2) = '\0'; 27187096Salfred } 27287096Salfred } 273165776Smjacob 27487096Salfred if (cbuff != NULL) { 27587096Salfred if (size_cbuff < objectsize+1) { 27687096Salfred debuglog("Cbuff not large enough." 27787096Salfred " Increase Size\n"); 27887096Salfred } 279165776Smjacob 28087096Salfred for(i=0;i<objectsize;i++) { 28187096Salfred if (*(object+i) >= 32 && *(object+i) <= 127) { 28287096Salfred *(cbuff+i) = *(object+i); 28387096Salfred } else { 28487096Salfred *(cbuff+i) = '.'; 28587096Salfred } 28687096Salfred } 28787096Salfred *(cbuff+i) = '\0'; 28887096Salfred } 28987096Salfred } 29087096Salfred} 29187096Salfred 29287096Salfredvoid 29387096Salfreddump_netobj(const struct netobj *nobj) 29487096Salfred{ 29587096Salfred char hbuff[MAXBUFFERSIZE*2]; 29687096Salfred char cbuff[MAXBUFFERSIZE]; 29787096Salfred 29887096Salfred if (debug_level < 2) { 29987096Salfred return; 30087096Salfred } 30187096Salfred 30287096Salfred if (nobj == NULL) { 30387096Salfred debuglog("Null netobj pointer\n"); 30487096Salfred } 30587096Salfred else if (nobj->n_len == 0) { 30687096Salfred debuglog("Size zero netobj\n"); 30787096Salfred } else { 30887096Salfred dump_static_object(nobj->n_bytes, nobj->n_len, 30987096Salfred hbuff, sizeof(hbuff), cbuff, sizeof(cbuff)); 31087096Salfred debuglog("netobj: len: %d data: %s ::: %s\n", 31187096Salfred nobj->n_len, hbuff, cbuff); 31287096Salfred } 31387096Salfred} 31487096Salfred 31592911Salfred/* #define DUMP_FILELOCK_VERBOSE */ 31687096Salfredvoid 31787096Salfreddump_filelock(const struct file_lock *fl) 31887096Salfred{ 31992911Salfred#ifdef DUMP_FILELOCK_VERBOSE 32087096Salfred char hbuff[MAXBUFFERSIZE*2]; 32187096Salfred char cbuff[MAXBUFFERSIZE]; 32292911Salfred#endif 32387096Salfred 32487096Salfred if (debug_level < 2) { 32587096Salfred return; 32687096Salfred } 32787096Salfred 32887096Salfred if (fl != NULL) { 32987096Salfred debuglog("Dumping file lock structure @ %p\n", fl); 33087096Salfred 33192911Salfred#ifdef DUMP_FILELOCK_VERBOSE 33287096Salfred dump_static_object((unsigned char *)&fl->filehandle, 33387096Salfred sizeof(fl->filehandle), hbuff, sizeof(hbuff), 33487096Salfred cbuff, sizeof(cbuff)); 33587096Salfred debuglog("Filehandle: %8s ::: %8s\n", hbuff, cbuff); 33692911Salfred#endif 337165776Smjacob 33887096Salfred debuglog("Dumping nlm4_holder:\n" 33987096Salfred "exc: %x svid: %x offset:len %llx:%llx\n", 34087096Salfred fl->client.exclusive, fl->client.svid, 34187096Salfred fl->client.l_offset, fl->client.l_len); 34287096Salfred 34392911Salfred#ifdef DUMP_FILELOCK_VERBOSE 34487096Salfred debuglog("Dumping client identity:\n"); 34587096Salfred dump_netobj(&fl->client.oh); 346165776Smjacob 34787096Salfred debuglog("Dumping client cookie:\n"); 34887096Salfred dump_netobj(&fl->client_cookie); 349165776Smjacob 350168640Skuriyama debuglog("nsm: %d status: %d flags: %d svid: %x" 351168640Skuriyama " client_name: %s\n", fl->nsm_status, fl->status, 352168640Skuriyama fl->flags, fl->client.svid, fl->client_name); 35392911Salfred#endif 35487096Salfred } else { 35587096Salfred debuglog("NULL file lock structure\n"); 35687096Salfred } 35787096Salfred} 35887096Salfred 35987096Salfredvoid 36087096Salfredcopy_nlm4_lock_to_nlm4_holder(src, exclusive, dest) 36187096Salfred const struct nlm4_lock *src; 36287096Salfred const bool_t exclusive; 36387096Salfred struct nlm4_holder *dest; 36487096Salfred{ 36587096Salfred 36687096Salfred dest->exclusive = exclusive; 36787096Salfred dest->oh.n_len = src->oh.n_len; 36887096Salfred dest->oh.n_bytes = src->oh.n_bytes; 36987096Salfred dest->svid = src->svid; 37087096Salfred dest->l_offset = src->l_offset; 37187096Salfred dest->l_len = src->l_len; 37287096Salfred} 37387096Salfred 37487096Salfred 375132254Smrsize_t 376132254Smrstrnlen(const char *s, size_t len) 377132254Smr{ 378132254Smr size_t n; 379132254Smr 380132254Smr for (n = 0; s[n] != 0 && n < len; n++) 381132254Smr ; 382132254Smr return n; 383132254Smr} 384132254Smr 38574462Salfred/* 38687096Salfred * allocate_file_lock: Create a lock with the given parameters 38784923Salfred */ 38887096Salfred 38987096Salfredstruct file_lock * 390132254Smrallocate_file_lock(const netobj *lockowner, const netobj *matchcookie, 391132254Smr const struct sockaddr *addr, const char *caller_name) 39287096Salfred{ 39387096Salfred struct file_lock *newfl; 394132254Smr size_t n; 39587096Salfred 396132254Smr /* Beware of rubbish input! */ 397132254Smr n = strnlen(caller_name, SM_MAXSTRLEN); 398132254Smr if (n == SM_MAXSTRLEN) { 399132254Smr return NULL; 400132254Smr } 401132254Smr 402132254Smr newfl = malloc(sizeof(*newfl) - sizeof(newfl->client_name) + n + 1); 40387096Salfred if (newfl == NULL) { 40487096Salfred return NULL; 40587096Salfred } 406132254Smr bzero(newfl, sizeof(*newfl) - sizeof(newfl->client_name)); 407132254Smr memcpy(newfl->client_name, caller_name, n); 408132254Smr newfl->client_name[n] = 0; 40987096Salfred 41087096Salfred newfl->client.oh.n_bytes = malloc(lockowner->n_len); 41187096Salfred if (newfl->client.oh.n_bytes == NULL) { 41287096Salfred free(newfl); 41387096Salfred return NULL; 41487096Salfred } 41587096Salfred newfl->client.oh.n_len = lockowner->n_len; 41687096Salfred bcopy(lockowner->n_bytes, newfl->client.oh.n_bytes, lockowner->n_len); 41787096Salfred 41887096Salfred newfl->client_cookie.n_bytes = malloc(matchcookie->n_len); 41987096Salfred if (newfl->client_cookie.n_bytes == NULL) { 42087096Salfred free(newfl->client.oh.n_bytes); 42187096Salfred free(newfl); 42287096Salfred return NULL; 42387096Salfred } 42487096Salfred newfl->client_cookie.n_len = matchcookie->n_len; 42587096Salfred bcopy(matchcookie->n_bytes, newfl->client_cookie.n_bytes, matchcookie->n_len); 42687096Salfred 427132254Smr newfl->addr = malloc(addr->sa_len); 428132254Smr if (newfl->addr == NULL) { 429132254Smr free(newfl->client_cookie.n_bytes); 430132254Smr free(newfl->client.oh.n_bytes); 431132254Smr free(newfl); 432132254Smr return NULL; 433132254Smr } 434132254Smr memcpy(newfl->addr, addr, addr->sa_len); 435132254Smr 43687096Salfred return newfl; 43787096Salfred} 43887096Salfred 43987096Salfred/* 44087096Salfred * file_file_lock: Force creation of a valid file lock 44187096Salfred */ 44287096Salfredvoid 44387199Salfredfill_file_lock(struct file_lock *fl, const fhandle_t *fh, 444132254Smr const bool_t exclusive, const int32_t svid, 445132254Smr const u_int64_t offset, const u_int64_t len, 44687199Salfred const int state, const int status, const int flags, const int blocking) 44787096Salfred{ 44887096Salfred bcopy(fh, &fl->filehandle, sizeof(fhandle_t)); 44987096Salfred 45087096Salfred fl->client.exclusive = exclusive; 45187096Salfred fl->client.svid = svid; 45287096Salfred fl->client.l_offset = offset; 45387096Salfred fl->client.l_len = len; 45487096Salfred 45587096Salfred fl->nsm_status = state; 45687096Salfred fl->status = status; 45787096Salfred fl->flags = flags; 45887096Salfred fl->blocking = blocking; 45987096Salfred} 46087096Salfred 46187096Salfred/* 46287096Salfred * deallocate_file_lock: Free all storage associated with a file lock 46387096Salfred */ 46487096Salfredvoid 46587096Salfreddeallocate_file_lock(struct file_lock *fl) 46687096Salfred{ 467132254Smr free(fl->addr); 46887096Salfred free(fl->client.oh.n_bytes); 46987096Salfred free(fl->client_cookie.n_bytes); 47087096Salfred free(fl); 47187096Salfred} 47287096Salfred 47387096Salfred/* 47487096Salfred * regions_overlap(): This function examines the two provided regions for 47587096Salfred * overlap. 47687096Salfred */ 47784923Salfredint 47884923Salfredregions_overlap(start1, len1, start2, len2) 47987096Salfred const u_int64_t start1, len1, start2, len2; 48084923Salfred{ 48187096Salfred u_int64_t d1,d2,d3,d4; 48287096Salfred enum split_status result; 48384923Salfred 48487096Salfred debuglog("Entering region overlap with vals: %llu:%llu--%llu:%llu\n", 48587096Salfred start1, len1, start2, len2); 48687096Salfred 48787096Salfred result = region_compare(start1, len1, start2, len2, 48887096Salfred &d1, &d2, &d3, &d4); 48987096Salfred 49087096Salfred debuglog("Exiting region overlap with val: %d\n",result); 49187096Salfred 49287096Salfred if (result == SPL_DISJOINT) { 49387096Salfred return 0; 49484923Salfred } else { 49587096Salfred return 1; 49684923Salfred } 49784923Salfred} 49887096Salfred 49984923Salfred/* 50087096Salfred * region_compare(): Examine lock regions and split appropriately 50187096Salfred * 50287096Salfred * XXX: Fix 64 bit overflow problems 503165776Smjacob * XXX: Check to make sure I got *ALL* the cases. 50487096Salfred * XXX: This DESPERATELY needs a regression test. 50574462Salfred */ 50687096Salfredenum split_status 50787096Salfredregion_compare(starte, lene, startu, lenu, 50887096Salfred start1, len1, start2, len2) 50987096Salfred const u_int64_t starte, lene, startu, lenu; 51087096Salfred u_int64_t *start1, *len1, *start2, *len2; 51187096Salfred{ 51287096Salfred /* 51387096Salfred * Please pay attention to the sequential exclusions 51487096Salfred * of the if statements!!! 51587096Salfred */ 51687096Salfred enum LFLAGS lflags; 51787096Salfred enum RFLAGS rflags; 51887096Salfred enum split_status retval; 51974462Salfred 52087096Salfred retval = SPL_DISJOINT; 52187096Salfred 52287096Salfred if (lene == 0 && lenu == 0) { 52387096Salfred /* Examine left edge of locker */ 524146445Scharnier lflags = LEDGE_INSIDE; 52587096Salfred if (startu < starte) { 52687096Salfred lflags = LEDGE_LEFT; 52787096Salfred } else if (startu == starte) { 52887096Salfred lflags = LEDGE_LBOUNDARY; 52987096Salfred } 53087096Salfred 53187096Salfred rflags = REDGE_RBOUNDARY; /* Both are infiinite */ 53287096Salfred 53387096Salfred if (lflags == LEDGE_INSIDE) { 53487096Salfred *start1 = starte; 53587096Salfred *len1 = startu - starte; 53687096Salfred } 53787096Salfred 53887096Salfred if (lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) { 53987096Salfred retval = SPL_CONTAINED; 54087096Salfred } else { 54187096Salfred retval = SPL_LOCK1; 54287096Salfred } 54387096Salfred } else if (lene == 0 && lenu != 0) { 54487096Salfred /* Established lock is infinite */ 54587096Salfred /* Examine left edge of unlocker */ 546146445Scharnier lflags = LEDGE_INSIDE; 54787096Salfred if (startu < starte) { 54887096Salfred lflags = LEDGE_LEFT; 54987096Salfred } else if (startu == starte) { 55087096Salfred lflags = LEDGE_LBOUNDARY; 55187096Salfred } 55287096Salfred 55387096Salfred /* Examine right edge of unlocker */ 55487096Salfred if (startu + lenu < starte) { 55587096Salfred /* Right edge of unlocker left of established lock */ 55687096Salfred rflags = REDGE_LEFT; 55787096Salfred return SPL_DISJOINT; 55887096Salfred } else if (startu + lenu == starte) { 55987096Salfred /* Right edge of unlocker on start of established lock */ 56087096Salfred rflags = REDGE_LBOUNDARY; 56187096Salfred return SPL_DISJOINT; 56287096Salfred } else { /* Infinifty is right of finity */ 56387096Salfred /* Right edge of unlocker inside established lock */ 56487096Salfred rflags = REDGE_INSIDE; 56587096Salfred } 56687096Salfred 56787096Salfred if (lflags == LEDGE_INSIDE) { 56887096Salfred *start1 = starte; 56987096Salfred *len1 = startu - starte; 57087096Salfred retval |= SPL_LOCK1; 57187096Salfred } 57287096Salfred 57387096Salfred if (rflags == REDGE_INSIDE) { 57487096Salfred /* Create right lock */ 57587096Salfred *start2 = startu+lenu; 57687096Salfred *len2 = 0; 57787096Salfred retval |= SPL_LOCK2; 57887096Salfred } 57987096Salfred } else if (lene != 0 && lenu == 0) { 58087096Salfred /* Unlocker is infinite */ 58187096Salfred /* Examine left edge of unlocker */ 582146445Scharnier lflags = LEDGE_RIGHT; 58387096Salfred if (startu < starte) { 58487096Salfred lflags = LEDGE_LEFT; 58587096Salfred retval = SPL_CONTAINED; 58687096Salfred return retval; 58787096Salfred } else if (startu == starte) { 58887096Salfred lflags = LEDGE_LBOUNDARY; 58987096Salfred retval = SPL_CONTAINED; 59087096Salfred return retval; 59187096Salfred } else if ((startu > starte) && (startu < starte + lene - 1)) { 59287096Salfred lflags = LEDGE_INSIDE; 59387096Salfred } else if (startu == starte + lene - 1) { 59487096Salfred lflags = LEDGE_RBOUNDARY; 59587096Salfred } else { /* startu > starte + lene -1 */ 59687096Salfred lflags = LEDGE_RIGHT; 59787096Salfred return SPL_DISJOINT; 59887096Salfred } 59987096Salfred 60087096Salfred rflags = REDGE_RIGHT; /* Infinity is right of finity */ 60187096Salfred 60287096Salfred if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { 60387096Salfred *start1 = starte; 60487096Salfred *len1 = startu - starte; 60587096Salfred retval |= SPL_LOCK1; 60687096Salfred return retval; 60787096Salfred } 60887096Salfred } else { 60987096Salfred /* Both locks are finite */ 61087096Salfred 61187096Salfred /* Examine left edge of unlocker */ 612146445Scharnier lflags = LEDGE_RIGHT; 61387096Salfred if (startu < starte) { 61487096Salfred lflags = LEDGE_LEFT; 61587096Salfred } else if (startu == starte) { 61687096Salfred lflags = LEDGE_LBOUNDARY; 61787096Salfred } else if ((startu > starte) && (startu < starte + lene - 1)) { 61887096Salfred lflags = LEDGE_INSIDE; 61987096Salfred } else if (startu == starte + lene - 1) { 62087096Salfred lflags = LEDGE_RBOUNDARY; 62187096Salfred } else { /* startu > starte + lene -1 */ 62287096Salfred lflags = LEDGE_RIGHT; 62387096Salfred return SPL_DISJOINT; 62487096Salfred } 62587096Salfred 62687096Salfred /* Examine right edge of unlocker */ 62787096Salfred if (startu + lenu < starte) { 62887096Salfred /* Right edge of unlocker left of established lock */ 62987096Salfred rflags = REDGE_LEFT; 63087096Salfred return SPL_DISJOINT; 63187096Salfred } else if (startu + lenu == starte) { 63287096Salfred /* Right edge of unlocker on start of established lock */ 63387096Salfred rflags = REDGE_LBOUNDARY; 63487096Salfred return SPL_DISJOINT; 63587096Salfred } else if (startu + lenu < starte + lene) { 63687096Salfred /* Right edge of unlocker inside established lock */ 63787096Salfred rflags = REDGE_INSIDE; 63887096Salfred } else if (startu + lenu == starte + lene) { 63987096Salfred /* Right edge of unlocker on right edge of established lock */ 64087096Salfred rflags = REDGE_RBOUNDARY; 64187096Salfred } else { /* startu + lenu > starte + lene */ 64287096Salfred /* Right edge of unlocker is right of established lock */ 64387096Salfred rflags = REDGE_RIGHT; 64487096Salfred } 64587096Salfred 64687096Salfred if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { 64787096Salfred /* Create left lock */ 64887096Salfred *start1 = starte; 64987096Salfred *len1 = (startu - starte); 65087096Salfred retval |= SPL_LOCK1; 65187096Salfred } 65287096Salfred 65387096Salfred if (rflags == REDGE_INSIDE) { 65487096Salfred /* Create right lock */ 65587096Salfred *start2 = startu+lenu; 65687096Salfred *len2 = starte+lene-(startu+lenu); 65787096Salfred retval |= SPL_LOCK2; 65887096Salfred } 65987096Salfred 66087096Salfred if ((lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) && 66187096Salfred (rflags == REDGE_RBOUNDARY || rflags == REDGE_RIGHT)) { 66287096Salfred retval = SPL_CONTAINED; 66387096Salfred } 66487096Salfred } 66587096Salfred return retval; 66687096Salfred} 66787096Salfred 66887096Salfred/* 66987096Salfred * same_netobj: Compares the apprpriate bits of a netobj for identity 67087096Salfred */ 67187096Salfredint 67287096Salfredsame_netobj(const netobj *n0, const netobj *n1) 67374462Salfred{ 67487096Salfred int retval; 67574462Salfred 67687096Salfred retval = 0; 67774462Salfred 67887096Salfred debuglog("Entering netobj identity check\n"); 67987096Salfred 68087096Salfred if (n0->n_len == n1->n_len) { 68187096Salfred debuglog("Preliminary length check passed\n"); 68287096Salfred retval = !bcmp(n0->n_bytes, n1->n_bytes, n0->n_len); 68387096Salfred debuglog("netobj %smatch\n", retval ? "" : "mis"); 68487096Salfred } 685165776Smjacob 68687096Salfred return (retval); 68787096Salfred} 68887096Salfred 68987096Salfred/* 69087096Salfred * same_filelock_identity: Compares the appropriate bits of a file_lock 69187096Salfred */ 69287096Salfredint 69387096Salfredsame_filelock_identity(fl0, fl1) 69487096Salfred const struct file_lock *fl0, *fl1; 69587096Salfred{ 69687096Salfred int retval; 69787096Salfred 69887096Salfred retval = 0; 69987096Salfred 70087096Salfred debuglog("Checking filelock identity\n"); 70187096Salfred 70287096Salfred /* 70387096Salfred * Check process ids and host information. 70487096Salfred */ 70587096Salfred retval = (fl0->client.svid == fl1->client.svid && 70687096Salfred same_netobj(&(fl0->client.oh), &(fl1->client.oh))); 70787096Salfred 70887096Salfred debuglog("Exiting checking filelock identity: retval: %d\n",retval); 70987096Salfred 71087096Salfred return (retval); 71187096Salfred} 71287096Salfred 71387096Salfred/* 71487096Salfred * Below here are routines associated with manipulating the NFS 71587096Salfred * lock list. 71687096Salfred */ 71787096Salfred 71887096Salfred/* 71987096Salfred * get_lock_matching_unlock: Return a lock which matches the given unlock lock 72087096Salfred * or NULL otehrwise 72187096Salfred * XXX: It is a shame that this duplicates so much code from test_nfslock. 72287096Salfred */ 72387096Salfredstruct file_lock * 72487096Salfredget_lock_matching_unlock(const struct file_lock *fl) 72587096Salfred{ 72687096Salfred struct file_lock *ifl; /* Iterator */ 72787096Salfred 728168640Skuriyama debuglog("Entering get_lock_matching_unlock\n"); 72987096Salfred debuglog("********Dump of fl*****************\n"); 73087096Salfred dump_filelock(fl); 73187096Salfred 73287096Salfred LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 73387096Salfred debuglog("Pointer to file lock: %p\n",ifl); 73487096Salfred 73587096Salfred debuglog("****Dump of ifl****\n"); 73687096Salfred dump_filelock(ifl); 73787096Salfred debuglog("*******************\n"); 73887096Salfred 73987096Salfred /* 74087096Salfred * XXX: It is conceivable that someone could use the NLM RPC 74187096Salfred * system to directly access filehandles. This may be a 74287096Salfred * security hazard as the filehandle code may bypass normal 74387096Salfred * file access controls 74487096Salfred */ 74587096Salfred if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t))) 74674462Salfred continue; 74787096Salfred 748168640Skuriyama debuglog("get_lock_matching_unlock: Filehandles match, " 74987096Salfred "checking regions\n"); 75087096Salfred 75187096Salfred /* Filehandles match, check for region overlap */ 75287096Salfred if (!regions_overlap(fl->client.l_offset, fl->client.l_len, 75387096Salfred ifl->client.l_offset, ifl->client.l_len)) 75487096Salfred continue; 755165776Smjacob 756168640Skuriyama debuglog("get_lock_matching_unlock: Region overlap" 75787096Salfred " found %llu : %llu -- %llu : %llu\n", 75887096Salfred fl->client.l_offset,fl->client.l_len, 75987096Salfred ifl->client.l_offset,ifl->client.l_len); 76087096Salfred 76187096Salfred /* Regions overlap, check the identity */ 76287096Salfred if (!same_filelock_identity(fl,ifl)) 76387096Salfred continue; 76487096Salfred 765168640Skuriyama debuglog("get_lock_matching_unlock: Duplicate lock id. Granting\n"); 76687096Salfred return (ifl); 76787096Salfred } 76887096Salfred 769168640Skuriyama debuglog("Exiting bet_lock_matching_unlock\n"); 77087096Salfred 77187096Salfred return (NULL); 77287096Salfred} 77387096Salfred 77487096Salfred/* 77587096Salfred * test_nfslock: check for NFS lock in lock list 77687096Salfred * 77787096Salfred * This routine makes the following assumptions: 77887096Salfred * 1) Nothing will adjust the lock list during a lookup 77987096Salfred * 78087096Salfred * This routine has an intersting quirk which bit me hard. 78187096Salfred * The conflicting_fl is the pointer to the conflicting lock. 78287096Salfred * However, to modify the "*pointer* to the conflicting lock" rather 78387096Salfred * that the "conflicting lock itself" one must pass in a "pointer to 78487096Salfred * the pointer of the conflicting lock". Gross. 78587096Salfred */ 78687096Salfred 78787096Salfredenum nfslock_status 78887096Salfredtest_nfslock(const struct file_lock *fl, struct file_lock **conflicting_fl) 78987096Salfred{ 79087096Salfred struct file_lock *ifl; /* Iterator */ 79187096Salfred enum nfslock_status retval; 79287096Salfred 79387096Salfred debuglog("Entering test_nfslock\n"); 79487096Salfred 79587096Salfred retval = NFS_GRANTED; 79687096Salfred (*conflicting_fl) = NULL; 79787096Salfred 79887096Salfred debuglog("Entering lock search loop\n"); 79987096Salfred 80087096Salfred debuglog("***********************************\n"); 80187096Salfred debuglog("Dumping match filelock\n"); 80287096Salfred debuglog("***********************************\n"); 80387096Salfred dump_filelock(fl); 80487096Salfred debuglog("***********************************\n"); 80587096Salfred 80687096Salfred LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 80787096Salfred if (retval == NFS_DENIED) 80887096Salfred break; 80987096Salfred 81087096Salfred debuglog("Top of lock loop\n"); 81187096Salfred debuglog("Pointer to file lock: %p\n",ifl); 812165776Smjacob 81387096Salfred debuglog("***********************************\n"); 81487096Salfred debuglog("Dumping test filelock\n"); 81587096Salfred debuglog("***********************************\n"); 81687096Salfred dump_filelock(ifl); 81787096Salfred debuglog("***********************************\n"); 81887096Salfred 81984923Salfred /* 82087096Salfred * XXX: It is conceivable that someone could use the NLM RPC 82187096Salfred * system to directly access filehandles. This may be a 82287096Salfred * security hazard as the filehandle code may bypass normal 82387096Salfred * file access controls 82484923Salfred */ 82587096Salfred if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t))) 82674462Salfred continue; 82787096Salfred 82887096Salfred debuglog("test_nfslock: filehandle match found\n"); 82987096Salfred 83087096Salfred /* Filehandles match, check for region overlap */ 83187096Salfred if (!regions_overlap(fl->client.l_offset, fl->client.l_len, 83287096Salfred ifl->client.l_offset, ifl->client.l_len)) 83387096Salfred continue; 83487096Salfred 83587096Salfred debuglog("test_nfslock: Region overlap found" 83687096Salfred " %llu : %llu -- %llu : %llu\n", 83787096Salfred fl->client.l_offset,fl->client.l_len, 83887096Salfred ifl->client.l_offset,ifl->client.l_len); 83987096Salfred 84087096Salfred /* Regions overlap, check the exclusivity */ 84187096Salfred if (!(fl->client.exclusive || ifl->client.exclusive)) 84287096Salfred continue; 843165776Smjacob 84487096Salfred debuglog("test_nfslock: Exclusivity failure: %d %d\n", 84587096Salfred fl->client.exclusive, 84687096Salfred ifl->client.exclusive); 84787096Salfred 84887096Salfred if (same_filelock_identity(fl,ifl)) { 84987096Salfred debuglog("test_nfslock: Duplicate id. Granting\n"); 85087096Salfred (*conflicting_fl) = ifl; 85187096Salfred retval = NFS_GRANTED_DUPLICATE; 85287096Salfred } else { 85387096Salfred /* locking attempt fails */ 85487096Salfred debuglog("test_nfslock: Lock attempt failed\n"); 85587096Salfred debuglog("Desired lock\n"); 85687096Salfred dump_filelock(fl); 85787096Salfred debuglog("Conflicting lock\n"); 85887096Salfred dump_filelock(ifl); 85987096Salfred (*conflicting_fl) = ifl; 86087096Salfred retval = NFS_DENIED; 86184923Salfred } 86284923Salfred } 863165776Smjacob 86487096Salfred debuglog("Dumping file locks\n"); 86587096Salfred debuglog("Exiting test_nfslock\n"); 866165776Smjacob 86787096Salfred return (retval); 86887096Salfred} 86987096Salfred 87087096Salfred/* 87187096Salfred * lock_nfslock: attempt to create a lock in the NFS lock list 87287096Salfred * 87387096Salfred * This routine tests whether the lock will be granted and then adds 87487096Salfred * the entry to the lock list if so. 875165776Smjacob * 87687096Salfred * Argument fl gets modified as its list housekeeping entries get modified 87787096Salfred * upon insertion into the NFS lock list 87887096Salfred * 87987096Salfred * This routine makes several assumptions: 88087096Salfred * 1) It is perfectly happy to grant a duplicate lock from the same pid. 88187096Salfred * While this seems to be intuitively wrong, it is required for proper 88287096Salfred * Posix semantics during unlock. It is absolutely imperative to not 88387096Salfred * unlock the main lock before the two child locks are established. Thus, 88487096Salfred * one has be be able to create duplicate locks over an existing lock 88587096Salfred * 2) It currently accepts duplicate locks from the same id,pid 88687096Salfred */ 88787096Salfred 88887096Salfredenum nfslock_status 88987096Salfredlock_nfslock(struct file_lock *fl) 89087096Salfred{ 89187096Salfred enum nfslock_status retval; 89287096Salfred struct file_lock *dummy_fl; 89387096Salfred 89487096Salfred dummy_fl = NULL; 89587096Salfred 89687096Salfred debuglog("Entering lock_nfslock...\n"); 89787096Salfred 89887096Salfred retval = test_nfslock(fl,&dummy_fl); 89987096Salfred 90087096Salfred if (retval == NFS_GRANTED || retval == NFS_GRANTED_DUPLICATE) { 90187096Salfred debuglog("Inserting lock...\n"); 90287096Salfred dump_filelock(fl); 90387096Salfred LIST_INSERT_HEAD(&nfslocklist_head, fl, nfslocklist); 90474462Salfred } 90587096Salfred 90687096Salfred debuglog("Exiting lock_nfslock...\n"); 90787096Salfred 90887096Salfred return (retval); 90974462Salfred} 91074462Salfred 91174462Salfred/* 91287096Salfred * delete_nfslock: delete an NFS lock list entry 91387096Salfred * 91487096Salfred * This routine is used to delete a lock out of the NFS lock list 91587096Salfred * without regard to status, underlying locks, regions or anything else 91687096Salfred * 91787096Salfred * Note that this routine *does not deallocate memory* of the lock. 91887096Salfred * It just disconnects it from the list. The lock can then be used 91987096Salfred * by other routines without fear of trashing the list. 92074462Salfred */ 92187096Salfred 92287096Salfredenum nfslock_status 92387096Salfreddelete_nfslock(struct file_lock *fl) 92474462Salfred{ 92574462Salfred 92687096Salfred LIST_REMOVE(fl, nfslocklist); 92787096Salfred 92887096Salfred return (NFS_GRANTED); 92987096Salfred} 93087096Salfred 93187096Salfredenum split_status 93287096Salfredsplit_nfslock(exist_lock, unlock_lock, left_lock, right_lock) 93387096Salfred const struct file_lock *exist_lock, *unlock_lock; 93487096Salfred struct file_lock **left_lock, **right_lock; 93587096Salfred{ 93687096Salfred u_int64_t start1, len1, start2, len2; 93787096Salfred enum split_status spstatus; 93887096Salfred 93987096Salfred spstatus = region_compare(exist_lock->client.l_offset, exist_lock->client.l_len, 94087096Salfred unlock_lock->client.l_offset, unlock_lock->client.l_len, 94187096Salfred &start1, &len1, &start2, &len2); 94287096Salfred 94387096Salfred if ((spstatus & SPL_LOCK1) != 0) { 944132254Smr *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie, exist_lock->addr, exist_lock->client_name); 94587096Salfred if (*left_lock == NULL) { 94687096Salfred debuglog("Unable to allocate resource for split 1\n"); 94787096Salfred return SPL_RESERR; 94887096Salfred } 94987096Salfred 95087199Salfred fill_file_lock(*left_lock, &exist_lock->filehandle, 95187096Salfred exist_lock->client.exclusive, exist_lock->client.svid, 95287096Salfred start1, len1, 953132254Smr exist_lock->nsm_status, 95487096Salfred exist_lock->status, exist_lock->flags, exist_lock->blocking); 95574462Salfred } 95687096Salfred 95787096Salfred if ((spstatus & SPL_LOCK2) != 0) { 958132254Smr *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie, exist_lock->addr, exist_lock->client_name); 95987096Salfred if (*right_lock == NULL) { 96087096Salfred debuglog("Unable to allocate resource for split 1\n"); 96187096Salfred if (*left_lock != NULL) { 96287096Salfred deallocate_file_lock(*left_lock); 96387096Salfred } 96487096Salfred return SPL_RESERR; 96587096Salfred } 96687096Salfred 96787199Salfred fill_file_lock(*right_lock, &exist_lock->filehandle, 96887096Salfred exist_lock->client.exclusive, exist_lock->client.svid, 96987096Salfred start2, len2, 970132254Smr exist_lock->nsm_status, 97187096Salfred exist_lock->status, exist_lock->flags, exist_lock->blocking); 97274462Salfred } 97387096Salfred 97487096Salfred return spstatus; 97587096Salfred} 97687096Salfred 97787096Salfredenum nfslock_status 97887096Salfredunlock_nfslock(fl, released_lock, left_lock, right_lock) 97987096Salfred const struct file_lock *fl; 98087096Salfred struct file_lock **released_lock; 98187096Salfred struct file_lock **left_lock; 98287096Salfred struct file_lock **right_lock; 98387096Salfred{ 98487096Salfred struct file_lock *mfl; /* Matching file lock */ 98587096Salfred enum nfslock_status retval; 98687096Salfred enum split_status spstatus; 98787096Salfred 98887096Salfred debuglog("Entering unlock_nfslock\n"); 98987096Salfred 99087096Salfred *released_lock = NULL; 99187096Salfred *left_lock = NULL; 99287096Salfred *right_lock = NULL; 99387096Salfred 99487096Salfred retval = NFS_DENIED_NOLOCK; 99587096Salfred 996132254Smr debuglog("Attempting to match lock...\n"); 99787096Salfred mfl = get_lock_matching_unlock(fl); 99887096Salfred 99987096Salfred if (mfl != NULL) { 100087096Salfred debuglog("Unlock matched. Querying for split\n"); 100187096Salfred 100287096Salfred spstatus = split_nfslock(mfl, fl, left_lock, right_lock); 100387096Salfred 100487096Salfred debuglog("Split returned %d %p %p %p %p\n",spstatus,mfl,fl,*left_lock,*right_lock); 100587096Salfred debuglog("********Split dumps********"); 100687096Salfred dump_filelock(mfl); 100787096Salfred dump_filelock(fl); 100887096Salfred dump_filelock(*left_lock); 100987096Salfred dump_filelock(*right_lock); 101087096Salfred debuglog("********End Split dumps********"); 101187096Salfred 101287096Salfred if (spstatus == SPL_RESERR) { 101387096Salfred if (*left_lock != NULL) { 101487096Salfred deallocate_file_lock(*left_lock); 101587096Salfred *left_lock = NULL; 101674462Salfred } 101787096Salfred 101887096Salfred if (*right_lock != NULL) { 101987096Salfred deallocate_file_lock(*right_lock); 102087096Salfred *right_lock = NULL; 102174462Salfred } 102287096Salfred 102387096Salfred return NFS_RESERR; 102474462Salfred } 102587096Salfred 102687096Salfred /* Insert new locks from split if required */ 102787096Salfred if (*left_lock != NULL) { 102887096Salfred debuglog("Split left activated\n"); 102987096Salfred LIST_INSERT_HEAD(&nfslocklist_head, *left_lock, nfslocklist); 103087096Salfred } 103187096Salfred 103287096Salfred if (*right_lock != NULL) { 103387096Salfred debuglog("Split right activated\n"); 103487096Salfred LIST_INSERT_HEAD(&nfslocklist_head, *right_lock, nfslocklist); 103587096Salfred } 103687096Salfred 103787096Salfred /* Unlock the lock since it matches identity */ 103887096Salfred LIST_REMOVE(mfl, nfslocklist); 103987096Salfred *released_lock = mfl; 104087096Salfred retval = NFS_GRANTED; 104174462Salfred } 104287096Salfred 104387096Salfred debuglog("Exiting unlock_nfslock\n"); 104487096Salfred 104574462Salfred return retval; 104674462Salfred} 104774462Salfred 104887096Salfred/* 104987096Salfred * Below here are the routines for manipulating the file lock directly 105087096Salfred * on the disk hardware itself 105187096Salfred */ 105287096Salfredenum hwlock_status 105387096Salfredlock_hwlock(struct file_lock *fl) 105474462Salfred{ 105587096Salfred struct monfile *imf,*nmf; 105687096Salfred int lflags, flerror; 105774462Salfred 105887096Salfred /* Scan to see if filehandle already present */ 105987096Salfred LIST_FOREACH(imf, &monfilelist_head, monfilelist) { 106087096Salfred if (bcmp(&fl->filehandle, &imf->filehandle, 106187096Salfred sizeof(fl->filehandle)) == 0) { 106287096Salfred /* imf is the correct filehandle */ 106374462Salfred break; 106487096Salfred } 106587096Salfred } 106687096Salfred 106787096Salfred /* 106887096Salfred * Filehandle already exists (we control the file) 106987096Salfred * *AND* NFS has already cleared the lock for availability 107087096Salfred * Grant it and bump the refcount. 107187096Salfred */ 107287096Salfred if (imf != NULL) { 107387096Salfred ++(imf->refcount); 107487096Salfred return (HW_GRANTED); 107587096Salfred } 107687096Salfred 107787096Salfred /* No filehandle found, create and go */ 107887096Salfred nmf = malloc(sizeof(struct monfile)); 107987096Salfred if (nmf == NULL) { 108087096Salfred debuglog("hwlock resource allocation failure\n"); 108187096Salfred return (HW_RESERR); 108287096Salfred } 108387096Salfred 108487096Salfred /* XXX: Is O_RDWR always the correct mode? */ 108587096Salfred nmf->fd = fhopen(&fl->filehandle, O_RDWR); 108687096Salfred if (nmf->fd < 0) { 108787096Salfred debuglog("fhopen failed (from %16s): %32s\n", 108887096Salfred fl->client_name, strerror(errno)); 108987096Salfred free(nmf); 109087096Salfred switch (errno) { 109187096Salfred case ESTALE: 109287096Salfred return (HW_STALEFH); 109387096Salfred case EROFS: 109487096Salfred return (HW_READONLY); 109587096Salfred default: 109687096Salfred return (HW_RESERR); 109787096Salfred } 109887096Salfred } 109987096Salfred 110087096Salfred /* File opened correctly, fill the monitor struct */ 110187096Salfred bcopy(&fl->filehandle, &nmf->filehandle, sizeof(fl->filehandle)); 110287096Salfred nmf->refcount = 1; 110387096Salfred nmf->exclusive = fl->client.exclusive; 110487096Salfred 110587096Salfred lflags = (nmf->exclusive == 1) ? 110687096Salfred (LOCK_EX | LOCK_NB) : (LOCK_SH | LOCK_NB); 110787096Salfred 110887096Salfred flerror = flock(nmf->fd, lflags); 110987096Salfred 111087096Salfred if (flerror != 0) { 111187096Salfred debuglog("flock failed (from %16s): %32s\n", 111287096Salfred fl->client_name, strerror(errno)); 111387096Salfred close(nmf->fd); 111487096Salfred free(nmf); 111587096Salfred switch (errno) { 111687096Salfred case EAGAIN: 111787096Salfred return (HW_DENIED); 111887096Salfred case ESTALE: 111987096Salfred return (HW_STALEFH); 112087096Salfred case EROFS: 112187096Salfred return (HW_READONLY); 112287096Salfred default: 112387096Salfred return (HW_RESERR); 112474462Salfred break; 112587096Salfred } 112687096Salfred } 112787096Salfred 112887096Salfred /* File opened and locked */ 112987096Salfred LIST_INSERT_HEAD(&monfilelist_head, nmf, monfilelist); 113087096Salfred 113187096Salfred debuglog("flock succeeded (from %16s)\n", fl->client_name); 113287096Salfred return (HW_GRANTED); 113387096Salfred} 1134165776Smjacob 113587096Salfredenum hwlock_status 113687096Salfredunlock_hwlock(const struct file_lock *fl) 113787096Salfred{ 113887096Salfred struct monfile *imf; 113987096Salfred 114087096Salfred debuglog("Entering unlock_hwlock\n"); 114187096Salfred debuglog("Entering loop interation\n"); 114287096Salfred 114387096Salfred /* Scan to see if filehandle already present */ 114487096Salfred LIST_FOREACH(imf, &monfilelist_head, monfilelist) { 114587096Salfred if (bcmp(&fl->filehandle, &imf->filehandle, 114687096Salfred sizeof(fl->filehandle)) == 0) { 114787096Salfred /* imf is the correct filehandle */ 114874462Salfred break; 114974462Salfred } 115074462Salfred } 115187096Salfred 115287096Salfred debuglog("Completed iteration. Proceeding\n"); 115387096Salfred 115487096Salfred if (imf == NULL) { 115587096Salfred /* No lock found */ 115687096Salfred debuglog("Exiting unlock_hwlock (HW_DENIED_NOLOCK)\n"); 115787096Salfred return (HW_DENIED_NOLOCK); 115887096Salfred } 115987096Salfred 116087096Salfred /* Lock found */ 116187096Salfred --imf->refcount; 116287096Salfred 116387096Salfred if (imf->refcount < 0) { 116487096Salfred debuglog("Negative hardware reference count\n"); 116587096Salfred } 116687096Salfred 116787096Salfred if (imf->refcount <= 0) { 116887096Salfred close(imf->fd); 116987096Salfred LIST_REMOVE(imf, monfilelist); 117087096Salfred free(imf); 117187096Salfred } 117287096Salfred debuglog("Exiting unlock_hwlock (HW_GRANTED)\n"); 117387096Salfred return (HW_GRANTED); 117474462Salfred} 117574462Salfred 117687096Salfredenum hwlock_status 117792911Salfredtest_hwlock(fl, conflicting_fl) 117892911Salfred const struct file_lock *fl __unused; 117992911Salfred struct file_lock **conflicting_fl __unused; 118087096Salfred{ 118187096Salfred 118287096Salfred /* 118387096Salfred * XXX: lock tests on hardware are not required until 118487096Salfred * true partial file testing is done on the underlying file 118587096Salfred */ 118687096Salfred return (HW_RESERR); 118787096Salfred} 118887096Salfred 118987096Salfred 119087096Salfred 119187096Salfred/* 119287096Salfred * Below here are routines for manipulating blocked lock requests 119387096Salfred * They should only be called from the XXX_partialfilelock routines 119487096Salfred * if at all possible 119587096Salfred */ 119687096Salfred 1197165775Smjacobint 1198165775Smjacobduplicate_block(struct file_lock *fl) 1199165775Smjacob{ 1200216603Suqs struct file_lock *ifl; 1201165775Smjacob int retval = 0; 1202165775Smjacob 1203165775Smjacob debuglog("Entering duplicate_block"); 1204165775Smjacob 1205165775Smjacob /* 1206165775Smjacob * Is this lock request already on the blocking list? 1207166054Sbrueffer * Consider it a dupe if the file handles, offset, length, 1208165775Smjacob * exclusivity and client match. 1209165775Smjacob */ 1210165775Smjacob LIST_FOREACH(ifl, &blockedlocklist_head, nfslocklist) { 1211165775Smjacob if (!bcmp(&fl->filehandle, &ifl->filehandle, 1212165775Smjacob sizeof(fhandle_t)) && 1213165775Smjacob fl->client.exclusive == ifl->client.exclusive && 1214165775Smjacob fl->client.l_offset == ifl->client.l_offset && 1215165775Smjacob fl->client.l_len == ifl->client.l_len && 1216165775Smjacob same_filelock_identity(fl, ifl)) { 1217165775Smjacob retval = 1; 1218165775Smjacob break; 1219165775Smjacob } 1220165775Smjacob } 1221165775Smjacob 1222165775Smjacob debuglog("Exiting duplicate_block: %s\n", retval ? "already blocked" 1223165775Smjacob : "not already blocked"); 1224165775Smjacob return retval; 1225165775Smjacob} 1226165775Smjacob 122774462Salfredvoid 122887096Salfredadd_blockingfilelock(struct file_lock *fl) 122974462Salfred{ 123087096Salfred debuglog("Entering add_blockingfilelock\n"); 123187096Salfred 123287096Salfred /* 1233165775Smjacob * A blocking lock request _should_ never be duplicated as a client 1234165775Smjacob * that is already blocked shouldn't be able to request another 1235165775Smjacob * lock. Alas, there are some buggy clients that do request the same 1236165775Smjacob * lock repeatedly. Make sure only unique locks are on the blocked 1237165775Smjacob * lock list. 1238165775Smjacob */ 1239165775Smjacob if (duplicate_block(fl)) { 1240165775Smjacob debuglog("Exiting add_blockingfilelock: already blocked\n"); 1241165775Smjacob return; 1242165775Smjacob } 1243165775Smjacob 1244165775Smjacob /* 124587096Salfred * Clear the blocking flag so that it can be reused without 124687096Salfred * adding it to the blocking queue a second time 124787096Salfred */ 124887096Salfred 124987096Salfred fl->blocking = 0; 125087096Salfred LIST_INSERT_HEAD(&blockedlocklist_head, fl, nfslocklist); 125187096Salfred 1252165775Smjacob debuglog("Exiting add_blockingfilelock: added blocked lock\n"); 125374462Salfred} 125474462Salfred 125574462Salfredvoid 125687096Salfredremove_blockingfilelock(struct file_lock *fl) 125774462Salfred{ 125874462Salfred 125987096Salfred debuglog("Entering remove_blockingfilelock\n"); 126087096Salfred 126187096Salfred LIST_REMOVE(fl, nfslocklist); 126287096Salfred 126387096Salfred debuglog("Exiting remove_blockingfilelock\n"); 126487096Salfred} 126587096Salfred 126687096Salfredvoid 126787096Salfredclear_blockingfilelock(const char *hostname) 126887096Salfred{ 126987096Salfred struct file_lock *ifl,*nfl; 127087096Salfred 127187096Salfred /* 127287096Salfred * Normally, LIST_FOREACH is called for, but since 127387096Salfred * the current element *is* the iterator, deleting it 127487096Salfred * would mess up the iteration. Thus, a next element 127587096Salfred * must be used explicitly 127687096Salfred */ 127787096Salfred 127887096Salfred ifl = LIST_FIRST(&blockedlocklist_head); 127987096Salfred 128087096Salfred while (ifl != NULL) { 128187096Salfred nfl = LIST_NEXT(ifl, nfslocklist); 128287096Salfred 128387096Salfred if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { 128487096Salfred remove_blockingfilelock(ifl); 128587096Salfred deallocate_file_lock(ifl); 128674462Salfred } 128787096Salfred 128887096Salfred ifl = nfl; 128987096Salfred } 129087096Salfred} 129187096Salfred 129287096Salfredvoid 129387096Salfredretry_blockingfilelocklist(void) 129487096Salfred{ 129587096Salfred /* Retry all locks in the blocked list */ 1296132254Smr struct file_lock *ifl, *nfl; /* Iterator */ 129787096Salfred enum partialfilelock_status pflstatus; 129887096Salfred 129987096Salfred debuglog("Entering retry_blockingfilelocklist\n"); 130087096Salfred 1301132254Smr LIST_FOREACH_SAFE(ifl, &blockedlocklist_head, nfslocklist, nfl) { 130287096Salfred debuglog("Iterator choice %p\n",ifl); 130387096Salfred debuglog("Next iterator choice %p\n",nfl); 130487096Salfred 130587096Salfred /* 130687096Salfred * SUBTLE BUG: The file_lock must be removed from the 130787096Salfred * old list so that it's list pointers get disconnected 130887096Salfred * before being allowed to participate in the new list 130987096Salfred * which will automatically add it in if necessary. 131087096Salfred */ 131187096Salfred 131287096Salfred LIST_REMOVE(ifl, nfslocklist); 131387096Salfred pflstatus = lock_partialfilelock(ifl); 1314165776Smjacob 131587096Salfred if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) { 131687096Salfred debuglog("Granted blocked lock\n"); 131787096Salfred /* lock granted and is now being used */ 131887096Salfred send_granted(ifl,0); 131987096Salfred } else { 1320132254Smr /* Reinsert lock back into blocked list */ 132187096Salfred debuglog("Replacing blocked lock\n"); 1322132254Smr LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist); 132374462Salfred } 132487096Salfred } 132587096Salfred 132687096Salfred debuglog("Exiting retry_blockingfilelocklist\n"); 132787096Salfred} 132887096Salfred 132987096Salfred/* 133087096Salfred * Below here are routines associated with manipulating all 133187096Salfred * aspects of the partial file locking system (list, hardware, etc.) 133287096Salfred */ 133387096Salfred 133487096Salfred/* 133587096Salfred * Please note that lock monitoring must be done at this level which 133687096Salfred * keeps track of *individual* lock requests on lock and unlock 133787096Salfred * 133887096Salfred * XXX: Split unlocking is going to make the unlock code miserable 133987096Salfred */ 134087096Salfred 134187096Salfred/* 134287096Salfred * lock_partialfilelock: 134387096Salfred * 134487096Salfred * Argument fl gets modified as its list housekeeping entries get modified 134587096Salfred * upon insertion into the NFS lock list 134687096Salfred * 134787096Salfred * This routine makes several assumptions: 134887096Salfred * 1) It (will) pass locks through to flock to lock the entire underlying file 134987096Salfred * and then parcel out NFS locks if it gets control of the file. 135087096Salfred * This matches the old rpc.lockd file semantics (except where it 135187096Salfred * is now more correct). It is the safe solution, but will cause 135287096Salfred * overly restrictive blocking if someone is trying to use the 135387096Salfred * underlying files without using NFS. This appears to be an 135487096Salfred * acceptable tradeoff since most people use standalone NFS servers. 135587096Salfred * XXX: The right solution is probably kevent combined with fcntl 135687096Salfred * 135787096Salfred * 2) Nothing modifies the lock lists between testing and granting 135887096Salfred * I have no idea whether this is a useful assumption or not 135987096Salfred */ 136087096Salfred 136187096Salfredenum partialfilelock_status 136287096Salfredlock_partialfilelock(struct file_lock *fl) 136387096Salfred{ 136487096Salfred enum partialfilelock_status retval; 136587096Salfred enum nfslock_status lnlstatus; 136687096Salfred enum hwlock_status hwstatus; 136787096Salfred 136887096Salfred debuglog("Entering lock_partialfilelock\n"); 136987096Salfred 137087096Salfred retval = PFL_DENIED; 137187096Salfred 137287096Salfred /* 137387096Salfred * Execute the NFS lock first, if possible, as it is significantly 137487096Salfred * easier and less expensive to undo than the filesystem lock 137587096Salfred */ 137687096Salfred 137787096Salfred lnlstatus = lock_nfslock(fl); 137887096Salfred 137987096Salfred switch (lnlstatus) { 138087096Salfred case NFS_GRANTED: 138187096Salfred case NFS_GRANTED_DUPLICATE: 138274462Salfred /* 138387096Salfred * At this point, the NFS lock is allocated and active. 138487096Salfred * Remember to clean it up if the hardware lock fails 138574462Salfred */ 138687096Salfred hwstatus = lock_hwlock(fl); 138787096Salfred 138887096Salfred switch (hwstatus) { 138987096Salfred case HW_GRANTED: 139087096Salfred case HW_GRANTED_DUPLICATE: 139187096Salfred debuglog("HW GRANTED\n"); 139287096Salfred /* 139387096Salfred * XXX: Fixme: Check hwstatus for duplicate when 139487096Salfred * true partial file locking and accounting is 1395165776Smjacob * done on the hardware. 139687096Salfred */ 139787096Salfred if (lnlstatus == NFS_GRANTED_DUPLICATE) { 139887096Salfred retval = PFL_GRANTED_DUPLICATE; 139987096Salfred } else { 140087096Salfred retval = PFL_GRANTED; 140187096Salfred } 140287096Salfred monitor_lock_host(fl->client_name); 140387096Salfred break; 140487096Salfred case HW_RESERR: 140587096Salfred debuglog("HW RESERR\n"); 140687096Salfred retval = PFL_HWRESERR; 140787096Salfred break; 140887096Salfred case HW_DENIED: 140987096Salfred debuglog("HW DENIED\n"); 141087096Salfred retval = PFL_HWDENIED; 141187096Salfred break; 141287096Salfred default: 141387096Salfred debuglog("Unmatched hwstatus %d\n",hwstatus); 141487096Salfred break; 141574462Salfred } 141687096Salfred 141787096Salfred if (retval != PFL_GRANTED && 141887096Salfred retval != PFL_GRANTED_DUPLICATE) { 141987096Salfred /* Clean up the NFS lock */ 142087096Salfred debuglog("Deleting trial NFS lock\n"); 142187096Salfred delete_nfslock(fl); 142287096Salfred } 142387096Salfred break; 142487096Salfred case NFS_DENIED: 142587096Salfred retval = PFL_NFSDENIED; 142687096Salfred break; 142787096Salfred case NFS_RESERR: 142887096Salfred retval = PFL_NFSRESERR; 142987096Salfred default: 143087096Salfred debuglog("Unmatched lnlstatus %d\n"); 143187096Salfred retval = PFL_NFSDENIED_NOLOCK; 143287096Salfred break; 143387096Salfred } 143487096Salfred 143587096Salfred /* 143687096Salfred * By the time fl reaches here, it is completely free again on 143787096Salfred * failure. The NFS lock done before attempting the 143887096Salfred * hardware lock has been backed out 143987096Salfred */ 144087096Salfred 144187096Salfred if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) { 144287096Salfred /* Once last chance to check the lock */ 144387096Salfred if (fl->blocking == 1) { 1444115004Srwatson if (retval == PFL_NFSDENIED) { 1445115004Srwatson /* Queue the lock */ 1446115004Srwatson debuglog("BLOCKING LOCK RECEIVED\n"); 1447115004Srwatson retval = PFL_NFSBLOCKED; 1448115004Srwatson add_blockingfilelock(fl); 1449115004Srwatson dump_filelock(fl); 1450115004Srwatson } else { 1451115004Srwatson /* retval is okay as PFL_HWDENIED */ 1452115004Srwatson debuglog("BLOCKING LOCK DENIED IN HARDWARE\n"); 1453115004Srwatson dump_filelock(fl); 1454115004Srwatson } 145574462Salfred } else { 145687096Salfred /* Leave retval alone, it's already correct */ 145787096Salfred debuglog("Lock denied. Non-blocking failure\n"); 145887096Salfred dump_filelock(fl); 145987096Salfred } 146087096Salfred } 146187096Salfred 146287096Salfred debuglog("Exiting lock_partialfilelock\n"); 146387096Salfred 146487096Salfred return retval; 146587096Salfred} 146687096Salfred 146787096Salfred/* 146887096Salfred * unlock_partialfilelock: 146987096Salfred * 147087096Salfred * Given a file_lock, unlock all locks which match. 147187096Salfred * 147287096Salfred * Note that a given lock might have to unlock ITSELF! See 147387096Salfred * clear_partialfilelock for example. 147487096Salfred */ 147587096Salfred 147687096Salfredenum partialfilelock_status 147787096Salfredunlock_partialfilelock(const struct file_lock *fl) 147887096Salfred{ 147987096Salfred struct file_lock *lfl,*rfl,*releasedfl,*selffl; 148087096Salfred enum partialfilelock_status retval; 148187096Salfred enum nfslock_status unlstatus; 148287096Salfred enum hwlock_status unlhwstatus, lhwstatus; 148387096Salfred 148487096Salfred debuglog("Entering unlock_partialfilelock\n"); 148587096Salfred 148687096Salfred selffl = NULL; 148787096Salfred lfl = NULL; 148887096Salfred rfl = NULL; 148987096Salfred releasedfl = NULL; 149087096Salfred retval = PFL_DENIED; 149187096Salfred 149287096Salfred /* 149387096Salfred * There are significant overlap and atomicity issues 149487096Salfred * with partially releasing a lock. For example, releasing 149587096Salfred * part of an NFS shared lock does *not* always release the 149687096Salfred * corresponding part of the file since there is only one 149787096Salfred * rpc.lockd UID but multiple users could be requesting it 149887096Salfred * from NFS. Also, an unlock request should never allow 149987096Salfred * another process to gain a lock on the remaining parts. 150087096Salfred * ie. Always apply the new locks before releasing the 150187096Salfred * old one 150287096Salfred */ 150387096Salfred 150487096Salfred /* 150587096Salfred * Loop is required since multiple little locks 150687096Salfred * can be allocated and then deallocated with one 150787096Salfred * big unlock. 150887096Salfred * 150987096Salfred * The loop is required to be here so that the nfs & 151087096Salfred * hw subsystems do not need to communicate with one 151187096Salfred * one another 151287096Salfred */ 151387096Salfred 151487096Salfred do { 151587096Salfred debuglog("Value of releasedfl: %p\n",releasedfl); 151687096Salfred /* lfl&rfl are created *AND* placed into the NFS lock list if required */ 151787096Salfred unlstatus = unlock_nfslock(fl, &releasedfl, &lfl, &rfl); 151887096Salfred debuglog("Value of releasedfl: %p\n",releasedfl); 151987096Salfred 152087096Salfred 152187096Salfred /* XXX: This is grungy. It should be refactored to be cleaner */ 152287096Salfred if (lfl != NULL) { 152387096Salfred lhwstatus = lock_hwlock(lfl); 152487096Salfred if (lhwstatus != HW_GRANTED && 152587096Salfred lhwstatus != HW_GRANTED_DUPLICATE) { 152687096Salfred debuglog("HW duplicate lock failure for left split\n"); 152774462Salfred } 152887096Salfred monitor_lock_host(lfl->client_name); 152987096Salfred } 153087096Salfred 153187096Salfred if (rfl != NULL) { 153287096Salfred lhwstatus = lock_hwlock(rfl); 153387096Salfred if (lhwstatus != HW_GRANTED && 153487096Salfred lhwstatus != HW_GRANTED_DUPLICATE) { 153587096Salfred debuglog("HW duplicate lock failure for right split\n"); 153687096Salfred } 153787096Salfred monitor_lock_host(rfl->client_name); 153887096Salfred } 153987096Salfred 154087096Salfred switch (unlstatus) { 154187096Salfred case NFS_GRANTED: 154287096Salfred /* Attempt to unlock on the hardware */ 154387096Salfred debuglog("NFS unlock granted. Attempting hardware unlock\n"); 154487096Salfred 154587096Salfred /* This call *MUST NOT* unlock the two newly allocated locks */ 154687096Salfred unlhwstatus = unlock_hwlock(fl); 154787096Salfred debuglog("HW unlock returned with code %d\n",unlhwstatus); 1548165776Smjacob 154987096Salfred switch (unlhwstatus) { 155087096Salfred case HW_GRANTED: 155187096Salfred debuglog("HW unlock granted\n"); 155287096Salfred unmonitor_lock_host(releasedfl->client_name); 155387096Salfred retval = PFL_GRANTED; 155474462Salfred break; 155587096Salfred case HW_DENIED_NOLOCK: 155687096Salfred /* Huh?!?! This shouldn't happen */ 155787096Salfred debuglog("HW unlock denied no lock\n"); 155887096Salfred retval = PFL_HWRESERR; 155987096Salfred /* Break out of do-while */ 156087096Salfred unlstatus = NFS_RESERR; 156174462Salfred break; 156274462Salfred default: 156387096Salfred debuglog("HW unlock failed\n"); 156487096Salfred retval = PFL_HWRESERR; 156587096Salfred /* Break out of do-while */ 156687096Salfred unlstatus = NFS_RESERR; 156787096Salfred break; 156874462Salfred } 1569165776Smjacob 157087096Salfred debuglog("Exiting with status retval: %d\n",retval); 1571165776Smjacob 157287096Salfred retry_blockingfilelocklist(); 157387096Salfred break; 157487096Salfred case NFS_DENIED_NOLOCK: 157587096Salfred retval = PFL_GRANTED; 157687096Salfred debuglog("All locks cleaned out\n"); 157787096Salfred break; 157887096Salfred default: 157987096Salfred retval = PFL_NFSRESERR; 158087096Salfred debuglog("NFS unlock failure\n"); 158187096Salfred dump_filelock(fl); 158287096Salfred break; 158374462Salfred } 158487096Salfred 158587096Salfred if (releasedfl != NULL) { 158687096Salfred if (fl == releasedfl) { 158787096Salfred /* 158887096Salfred * XXX: YECHHH!!! Attempt to unlock self succeeded 158987096Salfred * but we can't deallocate the space yet. This is what 159087096Salfred * happens when you don't write malloc and free together 159187096Salfred */ 159287096Salfred debuglog("Attempt to unlock self\n"); 159387096Salfred selffl = releasedfl; 159487096Salfred } else { 159587096Salfred /* 159687096Salfred * XXX: this deallocation *still* needs to migrate closer 159787096Salfred * to the allocation code way up in get_lock or the allocation 159887096Salfred * code needs to migrate down (violation of "When you write 159987096Salfred * malloc you must write free") 160087096Salfred */ 1601165776Smjacob 160287096Salfred deallocate_file_lock(releasedfl); 160387096Salfred } 160487096Salfred } 160587096Salfred 160687096Salfred } while (unlstatus == NFS_GRANTED); 160787096Salfred 160887096Salfred if (selffl != NULL) { 160987096Salfred /* 161087096Salfred * This statement wipes out the incoming file lock (fl) 161187096Salfred * in spite of the fact that it is declared const 161287096Salfred */ 161387096Salfred debuglog("WARNING! Destroying incoming lock pointer\n"); 161487096Salfred deallocate_file_lock(selffl); 161574462Salfred } 161687096Salfred 161787096Salfred debuglog("Exiting unlock_partialfilelock\n"); 161887096Salfred 161987096Salfred return retval; 162074462Salfred} 162174462Salfred 162274462Salfred/* 162387096Salfred * clear_partialfilelock 162474462Salfred * 162587096Salfred * Normally called in response to statd state number change. 162687096Salfred * Wipe out all locks held by a host. As a bonus, the act of 162787096Salfred * doing so should automatically clear their statd entries and 162887096Salfred * unmonitor the host. 162974462Salfred */ 163074462Salfred 1631165776Smjacobvoid 163287096Salfredclear_partialfilelock(const char *hostname) 163387096Salfred{ 163487096Salfred struct file_lock *ifl, *nfl; 163587096Salfred 163687096Salfred /* Clear blocking file lock list */ 163787096Salfred clear_blockingfilelock(hostname); 163887096Salfred 163987096Salfred /* do all required unlocks */ 164087096Salfred /* Note that unlock can smash the current pointer to a lock */ 164187096Salfred 164287096Salfred /* 164387096Salfred * Normally, LIST_FOREACH is called for, but since 164487096Salfred * the current element *is* the iterator, deleting it 164587096Salfred * would mess up the iteration. Thus, a next element 164687096Salfred * must be used explicitly 164787096Salfred */ 164887096Salfred 164987096Salfred ifl = LIST_FIRST(&nfslocklist_head); 165087096Salfred 165187096Salfred while (ifl != NULL) { 165287096Salfred nfl = LIST_NEXT(ifl, nfslocklist); 165387096Salfred 165487096Salfred if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { 165587096Salfred /* Unlock destroys ifl out from underneath */ 165687096Salfred unlock_partialfilelock(ifl); 165787096Salfred /* ifl is NO LONGER VALID AT THIS POINT */ 165887096Salfred } 165987096Salfred ifl = nfl; 166087096Salfred } 166187096Salfred} 166287096Salfred 166387096Salfred/* 166487096Salfred * test_partialfilelock: 166587096Salfred */ 166687096Salfredenum partialfilelock_status 166787096Salfredtest_partialfilelock(const struct file_lock *fl, 166887096Salfred struct file_lock **conflicting_fl) 166987096Salfred{ 167087096Salfred enum partialfilelock_status retval; 167187096Salfred enum nfslock_status teststatus; 167287096Salfred 167387096Salfred debuglog("Entering testpartialfilelock...\n"); 167487096Salfred 167587096Salfred retval = PFL_DENIED; 167687096Salfred 167787096Salfred teststatus = test_nfslock(fl, conflicting_fl); 167887096Salfred debuglog("test_partialfilelock: teststatus %d\n",teststatus); 167987096Salfred 168087096Salfred if (teststatus == NFS_GRANTED || teststatus == NFS_GRANTED_DUPLICATE) { 168187096Salfred /* XXX: Add the underlying filesystem locking code */ 168287096Salfred retval = (teststatus == NFS_GRANTED) ? 168387096Salfred PFL_GRANTED : PFL_GRANTED_DUPLICATE; 168487096Salfred debuglog("Dumping locks...\n"); 168587096Salfred dump_filelock(fl); 168687096Salfred dump_filelock(*conflicting_fl); 168787096Salfred debuglog("Done dumping locks...\n"); 168887096Salfred } else { 168987096Salfred retval = PFL_NFSDENIED; 169087096Salfred debuglog("NFS test denied.\n"); 169187096Salfred dump_filelock(fl); 169287096Salfred debuglog("Conflicting.\n"); 169387096Salfred dump_filelock(*conflicting_fl); 169487096Salfred } 169587096Salfred 169687096Salfred debuglog("Exiting testpartialfilelock...\n"); 169787096Salfred 169887096Salfred return retval; 169987096Salfred} 170087096Salfred 170187096Salfred/* 170287096Salfred * Below here are routines associated with translating the partial file locking 170387096Salfred * codes into useful codes to send back to the NFS RPC messaging system 170487096Salfred */ 170587096Salfred 170687096Salfred/* 170787096Salfred * These routines translate the (relatively) useful return codes back onto 170887096Salfred * the few return codes which the nlm subsystems wishes to trasmit 170987096Salfred */ 171087096Salfred 171174462Salfredenum nlm_stats 171287096Salfreddo_test(struct file_lock *fl, struct file_lock **conflicting_fl) 171374462Salfred{ 171487096Salfred enum partialfilelock_status pfsret; 171587096Salfred enum nlm_stats retval; 171674462Salfred 171787096Salfred debuglog("Entering do_test...\n"); 171887096Salfred 171987096Salfred pfsret = test_partialfilelock(fl,conflicting_fl); 172087096Salfred 172187096Salfred switch (pfsret) { 172287096Salfred case PFL_GRANTED: 172387096Salfred debuglog("PFL test lock granted\n"); 172487096Salfred dump_filelock(fl); 172587096Salfred dump_filelock(*conflicting_fl); 172687096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 172787096Salfred break; 172887096Salfred case PFL_GRANTED_DUPLICATE: 172987096Salfred debuglog("PFL test lock granted--duplicate id detected\n"); 173087096Salfred dump_filelock(fl); 173187096Salfred dump_filelock(*conflicting_fl); 173287096Salfred debuglog("Clearing conflicting_fl for call semantics\n"); 173387096Salfred *conflicting_fl = NULL; 173487096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 173587096Salfred break; 173687096Salfred case PFL_NFSDENIED: 173787096Salfred case PFL_HWDENIED: 173887096Salfred debuglog("PFL test lock denied\n"); 173987096Salfred dump_filelock(fl); 174087096Salfred dump_filelock(*conflicting_fl); 174187096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; 174287096Salfred break; 174387096Salfred case PFL_NFSRESERR: 174487096Salfred case PFL_HWRESERR: 174587096Salfred debuglog("PFL test lock resource fail\n"); 174687096Salfred dump_filelock(fl); 174787096Salfred dump_filelock(*conflicting_fl); 174887096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; 174987096Salfred break; 175087096Salfred default: 175187096Salfred debuglog("PFL test lock *FAILED*\n"); 175287096Salfred dump_filelock(fl); 175387096Salfred dump_filelock(*conflicting_fl); 175487096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; 175587096Salfred break; 175687096Salfred } 175787096Salfred 175887096Salfred debuglog("Exiting do_test...\n"); 175987096Salfred 176087096Salfred return retval; 176187096Salfred} 176287096Salfred 176387096Salfred/* 176487096Salfred * do_lock: Try to acquire a lock 176587096Salfred * 176687096Salfred * This routine makes a distinction between NLM versions. I am pretty 176787096Salfred * convinced that this should be abstracted out and bounced up a level 176887096Salfred */ 176987096Salfred 177087096Salfredenum nlm_stats 177187096Salfreddo_lock(struct file_lock *fl) 177287096Salfred{ 177387096Salfred enum partialfilelock_status pfsret; 177487096Salfred enum nlm_stats retval; 177587096Salfred 177687096Salfred debuglog("Entering do_lock...\n"); 177787096Salfred 177887096Salfred pfsret = lock_partialfilelock(fl); 177987096Salfred 178087096Salfred switch (pfsret) { 178187096Salfred case PFL_GRANTED: 178287096Salfred debuglog("PFL lock granted"); 178387096Salfred dump_filelock(fl); 178487096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 178587096Salfred break; 178687096Salfred case PFL_GRANTED_DUPLICATE: 178787096Salfred debuglog("PFL lock granted--duplicate id detected"); 178887096Salfred dump_filelock(fl); 178987096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 179087096Salfred break; 179187096Salfred case PFL_NFSDENIED: 179287096Salfred case PFL_HWDENIED: 179387096Salfred debuglog("PFL_NFS lock denied"); 179487096Salfred dump_filelock(fl); 179587096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; 179687096Salfred break; 179787096Salfred case PFL_NFSBLOCKED: 179887096Salfred case PFL_HWBLOCKED: 179987096Salfred debuglog("PFL_NFS blocking lock denied. Queued.\n"); 180087096Salfred dump_filelock(fl); 180187096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_blocked : nlm_blocked; 180287096Salfred break; 180387096Salfred case PFL_NFSRESERR: 180487096Salfred case PFL_HWRESERR: 180587096Salfred debuglog("PFL lock resource alocation fail\n"); 180687096Salfred dump_filelock(fl); 180787096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; 180887096Salfred break; 180987096Salfred default: 181087096Salfred debuglog("PFL lock *FAILED*"); 181187096Salfred dump_filelock(fl); 181287096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; 181387096Salfred break; 181487096Salfred } 181587096Salfred 181687096Salfred debuglog("Exiting do_lock...\n"); 181787096Salfred 181887096Salfred return retval; 181987096Salfred} 182087096Salfred 182187096Salfredenum nlm_stats 182287096Salfreddo_unlock(struct file_lock *fl) 182387096Salfred{ 182487096Salfred enum partialfilelock_status pfsret; 182587096Salfred enum nlm_stats retval; 182687096Salfred 182787096Salfred debuglog("Entering do_unlock...\n"); 182887096Salfred pfsret = unlock_partialfilelock(fl); 182987096Salfred 183087096Salfred switch (pfsret) { 183187096Salfred case PFL_GRANTED: 183287096Salfred debuglog("PFL unlock granted"); 183387096Salfred dump_filelock(fl); 183487096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 183587096Salfred break; 183687096Salfred case PFL_NFSDENIED: 183787096Salfred case PFL_HWDENIED: 183887096Salfred debuglog("PFL_NFS unlock denied"); 183987096Salfred dump_filelock(fl); 184087096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; 184187096Salfred break; 184287096Salfred case PFL_NFSDENIED_NOLOCK: 184387096Salfred case PFL_HWDENIED_NOLOCK: 184487096Salfred debuglog("PFL_NFS no lock found\n"); 184587096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 184687096Salfred break; 184787096Salfred case PFL_NFSRESERR: 184887096Salfred case PFL_HWRESERR: 184987096Salfred debuglog("PFL unlock resource failure"); 185087096Salfred dump_filelock(fl); 185187096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; 185287096Salfred break; 185387096Salfred default: 185487096Salfred debuglog("PFL unlock *FAILED*"); 185587096Salfred dump_filelock(fl); 185687096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; 185787096Salfred break; 185887096Salfred } 185987096Salfred 186087096Salfred debuglog("Exiting do_unlock...\n"); 186187096Salfred 186287096Salfred return retval; 186387096Salfred} 186487096Salfred 186587096Salfred/* 186687096Salfred * do_clear 186787096Salfred * 186887096Salfred * This routine is non-existent because it doesn't have a return code. 186987096Salfred * It is here for completeness in case someone *does* need to do return 187087096Salfred * codes later. A decent compiler should optimize this away. 187187096Salfred */ 187287096Salfred 187387096Salfredvoid 187487096Salfreddo_clear(const char *hostname) 187587096Salfred{ 187687096Salfred 187787096Salfred clear_partialfilelock(hostname); 187887096Salfred} 187987096Salfred 188087096Salfred/* 188187096Salfred * The following routines are all called from the code which the 188287096Salfred * RPC layer invokes 188387096Salfred */ 188487096Salfred 188587096Salfred/* 188687096Salfred * testlock(): inform the caller if the requested lock would be granted 188787096Salfred * 188887096Salfred * returns NULL if lock would granted 188987096Salfred * returns pointer to a conflicting nlm4_holder if not 189087096Salfred */ 189187096Salfred 189287096Salfredstruct nlm4_holder * 189392911Salfredtestlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused) 189487096Salfred{ 189587096Salfred struct file_lock test_fl, *conflicting_fl; 189687096Salfred 189787096Salfred bzero(&test_fl, sizeof(test_fl)); 189887096Salfred 189987096Salfred bcopy(lock->fh.n_bytes, &(test_fl.filehandle), sizeof(fhandle_t)); 190087096Salfred copy_nlm4_lock_to_nlm4_holder(lock, exclusive, &test_fl.client); 190187096Salfred 190287096Salfred siglock(); 190387096Salfred do_test(&test_fl, &conflicting_fl); 1904165776Smjacob 190587096Salfred if (conflicting_fl == NULL) { 190687096Salfred debuglog("No conflicting lock found\n"); 190787096Salfred sigunlock(); 190887096Salfred return NULL; 190987096Salfred } else { 191087096Salfred debuglog("Found conflicting lock\n"); 191187096Salfred dump_filelock(conflicting_fl); 191287096Salfred sigunlock(); 191387096Salfred return (&conflicting_fl->client); 191487096Salfred } 191587096Salfred} 191687096Salfred 191787096Salfred/* 1918228990Suqs * getlock: try to acquire the lock. 191987096Salfred * If file is already locked and we can sleep, put the lock in the list with 192087096Salfred * status LKST_WAITING; it'll be processed later. 192187096Salfred * Otherwise try to lock. If we're allowed to block, fork a child which 192287096Salfred * will do the blocking lock. 192387096Salfred */ 192487096Salfred 192587096Salfredenum nlm_stats 192687096Salfredgetlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags) 192787096Salfred{ 192887096Salfred struct file_lock *newfl; 192987096Salfred enum nlm_stats retval; 193087096Salfred 193187096Salfred debuglog("Entering getlock...\n"); 193287096Salfred 193387096Salfred if (grace_expired == 0 && lckarg->reclaim == 0) 193487096Salfred return (flags & LOCK_V4) ? 193587096Salfred nlm4_denied_grace_period : nlm_denied_grace_period; 193687096Salfred 193787096Salfred /* allocate new file_lock for this request */ 1938132254Smr newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->cookie, 1939132254Smr (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf, lckarg->alock.caller_name); 194087096Salfred if (newfl == NULL) { 194187096Salfred syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno)); 194287096Salfred /* failed */ 194387096Salfred return (flags & LOCK_V4) ? 194487096Salfred nlm4_denied_nolocks : nlm_denied_nolocks; 194587096Salfred } 194687096Salfred 194787096Salfred if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) { 1948228990Suqs debuglog("received fhandle size %d, local size %d", 194987096Salfred lckarg->alock.fh.n_len, (int)sizeof(fhandle_t)); 195087096Salfred } 195187096Salfred 195287096Salfred fill_file_lock(newfl, (fhandle_t *)lckarg->alock.fh.n_bytes, 195387199Salfred lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset, 195487199Salfred lckarg->alock.l_len, 1955132254Smr lckarg->state, 0, flags, lckarg->block); 1956165776Smjacob 195787096Salfred /* 195887096Salfred * newfl is now fully constructed and deallocate_file_lock 195987096Salfred * can now be used to delete it 196087096Salfred */ 1961165776Smjacob 196287096Salfred siglock(); 196387096Salfred debuglog("Pointer to new lock is %p\n",newfl); 196487096Salfred 196587096Salfred retval = do_lock(newfl); 196687096Salfred 196787096Salfred debuglog("Pointer to new lock is %p\n",newfl); 196887096Salfred sigunlock(); 1969165776Smjacob 197087096Salfred switch (retval) 197187096Salfred { 197287096Salfred case nlm4_granted: 197387096Salfred /* case nlm_granted: is the same as nlm4_granted */ 197487096Salfred /* do_mon(lckarg->alock.caller_name); */ 197574462Salfred break; 197687096Salfred case nlm4_blocked: 197787096Salfred /* case nlm_blocked: is the same as nlm4_blocked */ 197887096Salfred /* do_mon(lckarg->alock.caller_name); */ 197974462Salfred break; 198074462Salfred default: 198187096Salfred deallocate_file_lock(newfl); 198287096Salfred break; 198374462Salfred } 198487096Salfred 198587096Salfred debuglog("Exiting getlock...\n"); 198687096Salfred 198787096Salfred return retval; 198887096Salfred} 198987096Salfred 199087096Salfred 199187096Salfred/* unlock a filehandle */ 199287096Salfredenum nlm_stats 199392911Salfredunlock(nlm4_lock *lock, const int flags __unused) 199487096Salfred{ 199587096Salfred struct file_lock fl; 199687096Salfred enum nlm_stats err; 1997165776Smjacob 199887096Salfred siglock(); 1999165776Smjacob 200087096Salfred debuglog("Entering unlock...\n"); 2001165776Smjacob 200287096Salfred bzero(&fl,sizeof(struct file_lock)); 200387096Salfred bcopy(lock->fh.n_bytes, &fl.filehandle, sizeof(fhandle_t)); 2004165776Smjacob 200587096Salfred copy_nlm4_lock_to_nlm4_holder(lock, 0, &fl.client); 2006165776Smjacob 200787096Salfred err = do_unlock(&fl); 2008165776Smjacob 200987096Salfred sigunlock(); 2010165776Smjacob 201187096Salfred debuglog("Exiting unlock...\n"); 2012165776Smjacob 201387096Salfred return err; 201487096Salfred} 201587096Salfred 201687096Salfred/* 2017165776Smjacob * XXX: The following monitor/unmonitor routines 201887096Salfred * have not been extensively tested (ie. no regression 201987096Salfred * script exists like for the locking sections 202087096Salfred */ 202187096Salfred 202287096Salfred/* 202387096Salfred * monitor_lock_host: monitor lock hosts locally with a ref count and 202487096Salfred * inform statd 202587096Salfred */ 202687096Salfredvoid 202787096Salfredmonitor_lock_host(const char *hostname) 202887096Salfred{ 202987096Salfred struct host *ihp, *nhp; 203087096Salfred struct mon smon; 203187096Salfred struct sm_stat_res sres; 203287096Salfred int rpcret, statflag; 2033132254Smr size_t n; 2034165776Smjacob 203587096Salfred rpcret = 0; 203687096Salfred statflag = 0; 203787096Salfred 203887096Salfred LIST_FOREACH(ihp, &hostlst_head, hostlst) { 203987096Salfred if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) { 204087096Salfred /* Host is already monitored, bump refcount */ 204187096Salfred ++ihp->refcnt; 204287096Salfred /* Host should only be in the monitor list once */ 204387096Salfred return; 204487096Salfred } 204574462Salfred } 204687096Salfred 204787096Salfred /* Host is not yet monitored, add it */ 2048132254Smr n = strnlen(hostname, SM_MAXSTRLEN); 2049132254Smr if (n == SM_MAXSTRLEN) { 2050132254Smr return; 2051132254Smr } 2052132254Smr nhp = malloc(sizeof(*nhp) - sizeof(nhp->name) + n + 1); 205387096Salfred if (nhp == NULL) { 205487096Salfred debuglog("Unable to allocate entry for statd mon\n"); 205587096Salfred return; 205674462Salfred } 205787096Salfred 205887096Salfred /* Allocated new host entry, now fill the fields */ 2059132254Smr memcpy(nhp->name, hostname, n); 2060132254Smr nhp->name[n] = 0; 206187096Salfred nhp->refcnt = 1; 206287096Salfred debuglog("Locally Monitoring host %16s\n",hostname); 2063165776Smjacob 206487096Salfred debuglog("Attempting to tell statd\n"); 2065165776Smjacob 206687096Salfred bzero(&smon,sizeof(smon)); 2067165776Smjacob 206887096Salfred smon.mon_id.mon_name = nhp->name; 2069161231Sthomas smon.mon_id.my_id.my_name = "localhost"; 207087096Salfred smon.mon_id.my_id.my_prog = NLM_PROG; 207187096Salfred smon.mon_id.my_id.my_vers = NLM_SM; 207287096Salfred smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; 2073165776Smjacob 2074121558Speter rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_MON, 2075121558Speter (xdrproc_t)xdr_mon, &smon, 2076121558Speter (xdrproc_t)xdr_sm_stat_res, &sres); 2077165776Smjacob 207887096Salfred if (rpcret == 0) { 207987096Salfred if (sres.res_stat == stat_fail) { 208087096Salfred debuglog("Statd call failed\n"); 208187096Salfred statflag = 0; 208287096Salfred } else { 208387096Salfred statflag = 1; 208474462Salfred } 208587096Salfred } else { 208687096Salfred debuglog("Rpc call to statd failed with return value: %d\n", 208787096Salfred rpcret); 208887096Salfred statflag = 0; 208974462Salfred } 2090165776Smjacob 209187096Salfred if (statflag == 1) { 209287096Salfred LIST_INSERT_HEAD(&hostlst_head, nhp, hostlst); 209387096Salfred } else { 209487096Salfred free(nhp); 209587096Salfred } 209687096Salfred 209787096Salfred} 209887096Salfred 209987096Salfred/* 210087096Salfred * unmonitor_lock_host: clear monitor ref counts and inform statd when gone 210187096Salfred */ 210287096Salfredvoid 2103121558Speterunmonitor_lock_host(char *hostname) 210487096Salfred{ 210587096Salfred struct host *ihp; 210687096Salfred struct mon_id smon_id; 210787096Salfred struct sm_stat smstat; 210887096Salfred int rpcret; 2109165776Smjacob 211087096Salfred rpcret = 0; 211187096Salfred 211287096Salfred for( ihp=LIST_FIRST(&hostlst_head); ihp != NULL; 211387096Salfred ihp=LIST_NEXT(ihp, hostlst)) { 211487096Salfred if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) { 211587096Salfred /* Host is monitored, bump refcount */ 211687096Salfred --ihp->refcnt; 211787096Salfred /* Host should only be in the monitor list once */ 211874462Salfred break; 211974462Salfred } 212074462Salfred } 212187096Salfred 212287096Salfred if (ihp == NULL) { 212387096Salfred debuglog("Could not find host %16s in mon list\n", hostname); 212487096Salfred return; 212587096Salfred } 212687096Salfred 212787096Salfred if (ihp->refcnt > 0) 212887096Salfred return; 212987096Salfred 213087096Salfred if (ihp->refcnt < 0) { 213187096Salfred debuglog("Negative refcount!: %d\n", 213287096Salfred ihp->refcnt); 213387096Salfred } 213487096Salfred 213587096Salfred debuglog("Attempting to unmonitor host %16s\n", hostname); 213687096Salfred 213787096Salfred bzero(&smon_id,sizeof(smon_id)); 213887096Salfred 213992975Salfred smon_id.mon_name = hostname; 214087096Salfred smon_id.my_id.my_name = "localhost"; 214187096Salfred smon_id.my_id.my_prog = NLM_PROG; 214287096Salfred smon_id.my_id.my_vers = NLM_SM; 214387096Salfred smon_id.my_id.my_proc = NLM_SM_NOTIFY; 2144165776Smjacob 2145121558Speter rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON, 2146125903Salfred (xdrproc_t)xdr_mon_id, &smon_id, 2147125903Salfred (xdrproc_t)xdr_sm_stat, &smstat); 2148165776Smjacob 214987096Salfred if (rpcret != 0) { 215087096Salfred debuglog("Rpc call to unmonitor statd failed with " 215187096Salfred " return value: %d\n", rpcret); 215287096Salfred } 215387096Salfred 215487096Salfred LIST_REMOVE(ihp, hostlst); 215587096Salfred free(ihp); 215674462Salfred} 215774462Salfred 215887096Salfred/* 215987096Salfred * notify: Clear all locks from a host if statd complains 216087096Salfred * 216187096Salfred * XXX: This routine has not been thoroughly tested. However, neither 216287096Salfred * had the old one been. It used to compare the statd crash state counter 216387096Salfred * to the current lock state. The upshot of this was that it basically 216487096Salfred * cleared all locks from the specified host 99% of the time (with the 216587096Salfred * other 1% being a bug). Consequently, the assumption is that clearing 216687096Salfred * all locks from a host when notified by statd is acceptable. 216787096Salfred * 216887096Salfred * Please note that this routine skips the usual level of redirection 216987096Salfred * through a do_* type routine. This introduces a possible level of 217087096Salfred * error and might better be written as do_notify and take this one out. 217187096Salfred 217287096Salfred */ 217387096Salfred 217474462Salfredvoid 217587096Salfrednotify(const char *hostname, const int state) 217687096Salfred{ 217787096Salfred debuglog("notify from %s, new state %d", hostname, state); 2178165776Smjacob 217987096Salfred siglock(); 218087096Salfred do_clear(hostname); 218187096Salfred sigunlock(); 218287096Salfred 218387096Salfred debuglog("Leaving notify\n"); 218487096Salfred} 218587096Salfred 218687096Salfredvoid 218774462Salfredsend_granted(fl, opcode) 218874462Salfred struct file_lock *fl; 218992911Salfred int opcode __unused; 219074462Salfred{ 219174462Salfred CLIENT *cli; 219274462Salfred static char dummy; 219374462Salfred struct timeval timeo; 219474462Salfred int success; 219574462Salfred static struct nlm_res retval; 219674462Salfred static struct nlm4_res retval4; 219774462Salfred 219887096Salfred debuglog("About to send granted on blocked lock\n"); 219987096Salfred 220074462Salfred cli = get_client(fl->addr, 220174462Salfred (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); 220274462Salfred if (cli == NULL) { 220374462Salfred syslog(LOG_NOTICE, "failed to get CLIENT for %s", 220474462Salfred fl->client_name); 220574462Salfred /* 220674462Salfred * We fail to notify remote that the lock has been granted. 220774462Salfred * The client will timeout and retry, the lock will be 220874462Salfred * granted at this time. 220974462Salfred */ 221074462Salfred return; 221174462Salfred } 221274462Salfred timeo.tv_sec = 0; 221374462Salfred timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ 221474462Salfred 221574462Salfred if (fl->flags & LOCK_V4) { 221674462Salfred static nlm4_testargs res; 221774462Salfred res.cookie = fl->client_cookie; 221874462Salfred res.exclusive = fl->client.exclusive; 221974462Salfred res.alock.caller_name = fl->client_name; 222074462Salfred res.alock.fh.n_len = sizeof(fhandle_t); 222174462Salfred res.alock.fh.n_bytes = (char*)&fl->filehandle; 222274462Salfred res.alock.oh = fl->client.oh; 222374462Salfred res.alock.svid = fl->client.svid; 222474462Salfred res.alock.l_offset = fl->client.l_offset; 222574462Salfred res.alock.l_len = fl->client.l_len; 222687096Salfred debuglog("sending v4 reply%s", 222787096Salfred (fl->flags & LOCK_ASYNC) ? " (async)":""); 222874462Salfred if (fl->flags & LOCK_ASYNC) { 222974462Salfred success = clnt_call(cli, NLM4_GRANTED_MSG, 2230121558Speter (xdrproc_t)xdr_nlm4_testargs, &res, 2231121558Speter (xdrproc_t)xdr_void, &dummy, timeo); 223274462Salfred } else { 223374462Salfred success = clnt_call(cli, NLM4_GRANTED, 2234121558Speter (xdrproc_t)xdr_nlm4_testargs, &res, 2235121558Speter (xdrproc_t)xdr_nlm4_res, &retval4, timeo); 223674462Salfred } 223774462Salfred } else { 223874462Salfred static nlm_testargs res; 223974462Salfred 224074462Salfred res.cookie = fl->client_cookie; 224174462Salfred res.exclusive = fl->client.exclusive; 224274462Salfred res.alock.caller_name = fl->client_name; 224374462Salfred res.alock.fh.n_len = sizeof(fhandle_t); 224474462Salfred res.alock.fh.n_bytes = (char*)&fl->filehandle; 224574462Salfred res.alock.oh = fl->client.oh; 224674462Salfred res.alock.svid = fl->client.svid; 224774462Salfred res.alock.l_offset = fl->client.l_offset; 224874462Salfred res.alock.l_len = fl->client.l_len; 224987096Salfred debuglog("sending v1 reply%s", 225087096Salfred (fl->flags & LOCK_ASYNC) ? " (async)":""); 225174462Salfred if (fl->flags & LOCK_ASYNC) { 225274462Salfred success = clnt_call(cli, NLM_GRANTED_MSG, 2253121558Speter (xdrproc_t)xdr_nlm_testargs, &res, 2254121558Speter (xdrproc_t)xdr_void, &dummy, timeo); 225574462Salfred } else { 225674462Salfred success = clnt_call(cli, NLM_GRANTED, 2257121558Speter (xdrproc_t)xdr_nlm_testargs, &res, 2258121558Speter (xdrproc_t)xdr_nlm_res, &retval, timeo); 225974462Salfred } 226074462Salfred } 226174462Salfred if (debug_level > 2) 226287096Salfred debuglog("clnt_call returns %d(%s) for granted", 226387096Salfred success, clnt_sperrno(success)); 226474462Salfred 226574462Salfred} 226674462Salfred 226787096Salfred/* 226887096Salfred * Routines below here have not been modified in the overhaul 226987096Salfred */ 227074462Salfred 227187096Salfred/* 227287096Salfred * Are these two routines still required since lockd is not spawning off 227387096Salfred * children to service locks anymore? Presumably they were originally 227487096Salfred * put in place to prevent a one child from changing the lock list out 227587096Salfred * from under another one. 227687096Salfred */ 227774462Salfred 227874462Salfredvoid 227987096Salfredsiglock(void) 228074462Salfred{ 228187096Salfred sigset_t block; 2282165776Smjacob 228387096Salfred sigemptyset(&block); 228487096Salfred sigaddset(&block, SIGCHLD); 228574462Salfred 228687096Salfred if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { 228787096Salfred syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); 228887096Salfred } 228974462Salfred} 229074462Salfred 229174462Salfredvoid 229287096Salfredsigunlock(void) 229374462Salfred{ 229487096Salfred sigset_t block; 2295165776Smjacob 229687096Salfred sigemptyset(&block); 229787096Salfred sigaddset(&block, SIGCHLD); 2298165776Smjacob 229987096Salfred if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { 230087096Salfred syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); 230187096Salfred } 230274462Salfred} 2303