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 7284923Salfred * to improve search/access effeciency. 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 */ 10174462Salfred#define LKST_PROCESSING 3 /* child is trying to aquire 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 } 49787096Salfred 49884923Salfred return (result); 49984923Salfred} 50087096Salfred 50184923Salfred/* 50287096Salfred * region_compare(): Examine lock regions and split appropriately 50387096Salfred * 50487096Salfred * XXX: Fix 64 bit overflow problems 505165776Smjacob * XXX: Check to make sure I got *ALL* the cases. 50687096Salfred * XXX: This DESPERATELY needs a regression test. 50774462Salfred */ 50887096Salfredenum split_status 50987096Salfredregion_compare(starte, lene, startu, lenu, 51087096Salfred start1, len1, start2, len2) 51187096Salfred const u_int64_t starte, lene, startu, lenu; 51287096Salfred u_int64_t *start1, *len1, *start2, *len2; 51387096Salfred{ 51487096Salfred /* 51587096Salfred * Please pay attention to the sequential exclusions 51687096Salfred * of the if statements!!! 51787096Salfred */ 51887096Salfred enum LFLAGS lflags; 51987096Salfred enum RFLAGS rflags; 52087096Salfred enum split_status retval; 52174462Salfred 52287096Salfred retval = SPL_DISJOINT; 52387096Salfred 52487096Salfred if (lene == 0 && lenu == 0) { 52587096Salfred /* Examine left edge of locker */ 526146445Scharnier lflags = LEDGE_INSIDE; 52787096Salfred if (startu < starte) { 52887096Salfred lflags = LEDGE_LEFT; 52987096Salfred } else if (startu == starte) { 53087096Salfred lflags = LEDGE_LBOUNDARY; 53187096Salfred } 53287096Salfred 53387096Salfred rflags = REDGE_RBOUNDARY; /* Both are infiinite */ 53487096Salfred 53587096Salfred if (lflags == LEDGE_INSIDE) { 53687096Salfred *start1 = starte; 53787096Salfred *len1 = startu - starte; 53887096Salfred } 53987096Salfred 54087096Salfred if (lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) { 54187096Salfred retval = SPL_CONTAINED; 54287096Salfred } else { 54387096Salfred retval = SPL_LOCK1; 54487096Salfred } 54587096Salfred } else if (lene == 0 && lenu != 0) { 54687096Salfred /* Established lock is infinite */ 54787096Salfred /* Examine left edge of unlocker */ 548146445Scharnier lflags = LEDGE_INSIDE; 54987096Salfred if (startu < starte) { 55087096Salfred lflags = LEDGE_LEFT; 55187096Salfred } else if (startu == starte) { 55287096Salfred lflags = LEDGE_LBOUNDARY; 55387096Salfred } 55487096Salfred 55587096Salfred /* Examine right edge of unlocker */ 55687096Salfred if (startu + lenu < starte) { 55787096Salfred /* Right edge of unlocker left of established lock */ 55887096Salfred rflags = REDGE_LEFT; 55987096Salfred return SPL_DISJOINT; 56087096Salfred } else if (startu + lenu == starte) { 56187096Salfred /* Right edge of unlocker on start of established lock */ 56287096Salfred rflags = REDGE_LBOUNDARY; 56387096Salfred return SPL_DISJOINT; 56487096Salfred } else { /* Infinifty is right of finity */ 56587096Salfred /* Right edge of unlocker inside established lock */ 56687096Salfred rflags = REDGE_INSIDE; 56787096Salfred } 56887096Salfred 56987096Salfred if (lflags == LEDGE_INSIDE) { 57087096Salfred *start1 = starte; 57187096Salfred *len1 = startu - starte; 57287096Salfred retval |= SPL_LOCK1; 57387096Salfred } 57487096Salfred 57587096Salfred if (rflags == REDGE_INSIDE) { 57687096Salfred /* Create right lock */ 57787096Salfred *start2 = startu+lenu; 57887096Salfred *len2 = 0; 57987096Salfred retval |= SPL_LOCK2; 58087096Salfred } 58187096Salfred } else if (lene != 0 && lenu == 0) { 58287096Salfred /* Unlocker is infinite */ 58387096Salfred /* Examine left edge of unlocker */ 584146445Scharnier lflags = LEDGE_RIGHT; 58587096Salfred if (startu < starte) { 58687096Salfred lflags = LEDGE_LEFT; 58787096Salfred retval = SPL_CONTAINED; 58887096Salfred return retval; 58987096Salfred } else if (startu == starte) { 59087096Salfred lflags = LEDGE_LBOUNDARY; 59187096Salfred retval = SPL_CONTAINED; 59287096Salfred return retval; 59387096Salfred } else if ((startu > starte) && (startu < starte + lene - 1)) { 59487096Salfred lflags = LEDGE_INSIDE; 59587096Salfred } else if (startu == starte + lene - 1) { 59687096Salfred lflags = LEDGE_RBOUNDARY; 59787096Salfred } else { /* startu > starte + lene -1 */ 59887096Salfred lflags = LEDGE_RIGHT; 59987096Salfred return SPL_DISJOINT; 60087096Salfred } 60187096Salfred 60287096Salfred rflags = REDGE_RIGHT; /* Infinity is right of finity */ 60387096Salfred 60487096Salfred if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { 60587096Salfred *start1 = starte; 60687096Salfred *len1 = startu - starte; 60787096Salfred retval |= SPL_LOCK1; 60887096Salfred return retval; 60987096Salfred } 61087096Salfred } else { 61187096Salfred /* Both locks are finite */ 61287096Salfred 61387096Salfred /* Examine left edge of unlocker */ 614146445Scharnier lflags = LEDGE_RIGHT; 61587096Salfred if (startu < starte) { 61687096Salfred lflags = LEDGE_LEFT; 61787096Salfred } else if (startu == starte) { 61887096Salfred lflags = LEDGE_LBOUNDARY; 61987096Salfred } else if ((startu > starte) && (startu < starte + lene - 1)) { 62087096Salfred lflags = LEDGE_INSIDE; 62187096Salfred } else if (startu == starte + lene - 1) { 62287096Salfred lflags = LEDGE_RBOUNDARY; 62387096Salfred } else { /* startu > starte + lene -1 */ 62487096Salfred lflags = LEDGE_RIGHT; 62587096Salfred return SPL_DISJOINT; 62687096Salfred } 62787096Salfred 62887096Salfred /* Examine right edge of unlocker */ 62987096Salfred if (startu + lenu < starte) { 63087096Salfred /* Right edge of unlocker left of established lock */ 63187096Salfred rflags = REDGE_LEFT; 63287096Salfred return SPL_DISJOINT; 63387096Salfred } else if (startu + lenu == starte) { 63487096Salfred /* Right edge of unlocker on start of established lock */ 63587096Salfred rflags = REDGE_LBOUNDARY; 63687096Salfred return SPL_DISJOINT; 63787096Salfred } else if (startu + lenu < starte + lene) { 63887096Salfred /* Right edge of unlocker inside established lock */ 63987096Salfred rflags = REDGE_INSIDE; 64087096Salfred } else if (startu + lenu == starte + lene) { 64187096Salfred /* Right edge of unlocker on right edge of established lock */ 64287096Salfred rflags = REDGE_RBOUNDARY; 64387096Salfred } else { /* startu + lenu > starte + lene */ 64487096Salfred /* Right edge of unlocker is right of established lock */ 64587096Salfred rflags = REDGE_RIGHT; 64687096Salfred } 64787096Salfred 64887096Salfred if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { 64987096Salfred /* Create left lock */ 65087096Salfred *start1 = starte; 65187096Salfred *len1 = (startu - starte); 65287096Salfred retval |= SPL_LOCK1; 65387096Salfred } 65487096Salfred 65587096Salfred if (rflags == REDGE_INSIDE) { 65687096Salfred /* Create right lock */ 65787096Salfred *start2 = startu+lenu; 65887096Salfred *len2 = starte+lene-(startu+lenu); 65987096Salfred retval |= SPL_LOCK2; 66087096Salfred } 66187096Salfred 66287096Salfred if ((lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) && 66387096Salfred (rflags == REDGE_RBOUNDARY || rflags == REDGE_RIGHT)) { 66487096Salfred retval = SPL_CONTAINED; 66587096Salfred } 66687096Salfred } 66787096Salfred return retval; 66887096Salfred} 66987096Salfred 67087096Salfred/* 67187096Salfred * same_netobj: Compares the apprpriate bits of a netobj for identity 67287096Salfred */ 67387096Salfredint 67487096Salfredsame_netobj(const netobj *n0, const netobj *n1) 67574462Salfred{ 67687096Salfred int retval; 67774462Salfred 67887096Salfred retval = 0; 67974462Salfred 68087096Salfred debuglog("Entering netobj identity check\n"); 68187096Salfred 68287096Salfred if (n0->n_len == n1->n_len) { 68387096Salfred debuglog("Preliminary length check passed\n"); 68487096Salfred retval = !bcmp(n0->n_bytes, n1->n_bytes, n0->n_len); 68587096Salfred debuglog("netobj %smatch\n", retval ? "" : "mis"); 68687096Salfred } 687165776Smjacob 68887096Salfred return (retval); 68987096Salfred} 69087096Salfred 69187096Salfred/* 69287096Salfred * same_filelock_identity: Compares the appropriate bits of a file_lock 69387096Salfred */ 69487096Salfredint 69587096Salfredsame_filelock_identity(fl0, fl1) 69687096Salfred const struct file_lock *fl0, *fl1; 69787096Salfred{ 69887096Salfred int retval; 69987096Salfred 70087096Salfred retval = 0; 70187096Salfred 70287096Salfred debuglog("Checking filelock identity\n"); 70387096Salfred 70487096Salfred /* 70587096Salfred * Check process ids and host information. 70687096Salfred */ 70787096Salfred retval = (fl0->client.svid == fl1->client.svid && 70887096Salfred same_netobj(&(fl0->client.oh), &(fl1->client.oh))); 70987096Salfred 71087096Salfred debuglog("Exiting checking filelock identity: retval: %d\n",retval); 71187096Salfred 71287096Salfred return (retval); 71387096Salfred} 71487096Salfred 71587096Salfred/* 71687096Salfred * Below here are routines associated with manipulating the NFS 71787096Salfred * lock list. 71887096Salfred */ 71987096Salfred 72087096Salfred/* 72187096Salfred * get_lock_matching_unlock: Return a lock which matches the given unlock lock 72287096Salfred * or NULL otehrwise 72387096Salfred * XXX: It is a shame that this duplicates so much code from test_nfslock. 72487096Salfred */ 72587096Salfredstruct file_lock * 72687096Salfredget_lock_matching_unlock(const struct file_lock *fl) 72787096Salfred{ 72887096Salfred struct file_lock *ifl; /* Iterator */ 72987096Salfred 730168640Skuriyama debuglog("Entering get_lock_matching_unlock\n"); 73187096Salfred debuglog("********Dump of fl*****************\n"); 73287096Salfred dump_filelock(fl); 73387096Salfred 73487096Salfred LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 73587096Salfred debuglog("Pointer to file lock: %p\n",ifl); 73687096Salfred 73787096Salfred debuglog("****Dump of ifl****\n"); 73887096Salfred dump_filelock(ifl); 73987096Salfred debuglog("*******************\n"); 74087096Salfred 74187096Salfred /* 74287096Salfred * XXX: It is conceivable that someone could use the NLM RPC 74387096Salfred * system to directly access filehandles. This may be a 74487096Salfred * security hazard as the filehandle code may bypass normal 74587096Salfred * file access controls 74687096Salfred */ 74787096Salfred if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t))) 74874462Salfred continue; 74987096Salfred 750168640Skuriyama debuglog("get_lock_matching_unlock: Filehandles match, " 75187096Salfred "checking regions\n"); 75287096Salfred 75387096Salfred /* Filehandles match, check for region overlap */ 75487096Salfred if (!regions_overlap(fl->client.l_offset, fl->client.l_len, 75587096Salfred ifl->client.l_offset, ifl->client.l_len)) 75687096Salfred continue; 757165776Smjacob 758168640Skuriyama debuglog("get_lock_matching_unlock: Region overlap" 75987096Salfred " found %llu : %llu -- %llu : %llu\n", 76087096Salfred fl->client.l_offset,fl->client.l_len, 76187096Salfred ifl->client.l_offset,ifl->client.l_len); 76287096Salfred 76387096Salfred /* Regions overlap, check the identity */ 76487096Salfred if (!same_filelock_identity(fl,ifl)) 76587096Salfred continue; 76687096Salfred 767168640Skuriyama debuglog("get_lock_matching_unlock: Duplicate lock id. Granting\n"); 76887096Salfred return (ifl); 76987096Salfred } 77087096Salfred 771168640Skuriyama debuglog("Exiting bet_lock_matching_unlock\n"); 77287096Salfred 77387096Salfred return (NULL); 77487096Salfred} 77587096Salfred 77687096Salfred/* 77787096Salfred * test_nfslock: check for NFS lock in lock list 77887096Salfred * 77987096Salfred * This routine makes the following assumptions: 78087096Salfred * 1) Nothing will adjust the lock list during a lookup 78187096Salfred * 78287096Salfred * This routine has an intersting quirk which bit me hard. 78387096Salfred * The conflicting_fl is the pointer to the conflicting lock. 78487096Salfred * However, to modify the "*pointer* to the conflicting lock" rather 78587096Salfred * that the "conflicting lock itself" one must pass in a "pointer to 78687096Salfred * the pointer of the conflicting lock". Gross. 78787096Salfred */ 78887096Salfred 78987096Salfredenum nfslock_status 79087096Salfredtest_nfslock(const struct file_lock *fl, struct file_lock **conflicting_fl) 79187096Salfred{ 79287096Salfred struct file_lock *ifl; /* Iterator */ 79387096Salfred enum nfslock_status retval; 79487096Salfred 79587096Salfred debuglog("Entering test_nfslock\n"); 79687096Salfred 79787096Salfred retval = NFS_GRANTED; 79887096Salfred (*conflicting_fl) = NULL; 79987096Salfred 80087096Salfred debuglog("Entering lock search loop\n"); 80187096Salfred 80287096Salfred debuglog("***********************************\n"); 80387096Salfred debuglog("Dumping match filelock\n"); 80487096Salfred debuglog("***********************************\n"); 80587096Salfred dump_filelock(fl); 80687096Salfred debuglog("***********************************\n"); 80787096Salfred 80887096Salfred LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { 80987096Salfred if (retval == NFS_DENIED) 81087096Salfred break; 81187096Salfred 81287096Salfred debuglog("Top of lock loop\n"); 81387096Salfred debuglog("Pointer to file lock: %p\n",ifl); 814165776Smjacob 81587096Salfred debuglog("***********************************\n"); 81687096Salfred debuglog("Dumping test filelock\n"); 81787096Salfred debuglog("***********************************\n"); 81887096Salfred dump_filelock(ifl); 81987096Salfred debuglog("***********************************\n"); 82087096Salfred 82184923Salfred /* 82287096Salfred * XXX: It is conceivable that someone could use the NLM RPC 82387096Salfred * system to directly access filehandles. This may be a 82487096Salfred * security hazard as the filehandle code may bypass normal 82587096Salfred * file access controls 82684923Salfred */ 82787096Salfred if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t))) 82874462Salfred continue; 82987096Salfred 83087096Salfred debuglog("test_nfslock: filehandle match found\n"); 83187096Salfred 83287096Salfred /* Filehandles match, check for region overlap */ 83387096Salfred if (!regions_overlap(fl->client.l_offset, fl->client.l_len, 83487096Salfred ifl->client.l_offset, ifl->client.l_len)) 83587096Salfred continue; 83687096Salfred 83787096Salfred debuglog("test_nfslock: Region overlap found" 83887096Salfred " %llu : %llu -- %llu : %llu\n", 83987096Salfred fl->client.l_offset,fl->client.l_len, 84087096Salfred ifl->client.l_offset,ifl->client.l_len); 84187096Salfred 84287096Salfred /* Regions overlap, check the exclusivity */ 84387096Salfred if (!(fl->client.exclusive || ifl->client.exclusive)) 84487096Salfred continue; 845165776Smjacob 84687096Salfred debuglog("test_nfslock: Exclusivity failure: %d %d\n", 84787096Salfred fl->client.exclusive, 84887096Salfred ifl->client.exclusive); 84987096Salfred 85087096Salfred if (same_filelock_identity(fl,ifl)) { 85187096Salfred debuglog("test_nfslock: Duplicate id. Granting\n"); 85287096Salfred (*conflicting_fl) = ifl; 85387096Salfred retval = NFS_GRANTED_DUPLICATE; 85487096Salfred } else { 85587096Salfred /* locking attempt fails */ 85687096Salfred debuglog("test_nfslock: Lock attempt failed\n"); 85787096Salfred debuglog("Desired lock\n"); 85887096Salfred dump_filelock(fl); 85987096Salfred debuglog("Conflicting lock\n"); 86087096Salfred dump_filelock(ifl); 86187096Salfred (*conflicting_fl) = ifl; 86287096Salfred retval = NFS_DENIED; 86384923Salfred } 86484923Salfred } 865165776Smjacob 86687096Salfred debuglog("Dumping file locks\n"); 86787096Salfred debuglog("Exiting test_nfslock\n"); 868165776Smjacob 86987096Salfred return (retval); 87087096Salfred} 87187096Salfred 87287096Salfred/* 87387096Salfred * lock_nfslock: attempt to create a lock in the NFS lock list 87487096Salfred * 87587096Salfred * This routine tests whether the lock will be granted and then adds 87687096Salfred * the entry to the lock list if so. 877165776Smjacob * 87887096Salfred * Argument fl gets modified as its list housekeeping entries get modified 87987096Salfred * upon insertion into the NFS lock list 88087096Salfred * 88187096Salfred * This routine makes several assumptions: 88287096Salfred * 1) It is perfectly happy to grant a duplicate lock from the same pid. 88387096Salfred * While this seems to be intuitively wrong, it is required for proper 88487096Salfred * Posix semantics during unlock. It is absolutely imperative to not 88587096Salfred * unlock the main lock before the two child locks are established. Thus, 88687096Salfred * one has be be able to create duplicate locks over an existing lock 88787096Salfred * 2) It currently accepts duplicate locks from the same id,pid 88887096Salfred */ 88987096Salfred 89087096Salfredenum nfslock_status 89187096Salfredlock_nfslock(struct file_lock *fl) 89287096Salfred{ 89387096Salfred enum nfslock_status retval; 89487096Salfred struct file_lock *dummy_fl; 89587096Salfred 89687096Salfred dummy_fl = NULL; 89787096Salfred 89887096Salfred debuglog("Entering lock_nfslock...\n"); 89987096Salfred 90087096Salfred retval = test_nfslock(fl,&dummy_fl); 90187096Salfred 90287096Salfred if (retval == NFS_GRANTED || retval == NFS_GRANTED_DUPLICATE) { 90387096Salfred debuglog("Inserting lock...\n"); 90487096Salfred dump_filelock(fl); 90587096Salfred LIST_INSERT_HEAD(&nfslocklist_head, fl, nfslocklist); 90674462Salfred } 90787096Salfred 90887096Salfred debuglog("Exiting lock_nfslock...\n"); 90987096Salfred 91087096Salfred return (retval); 91174462Salfred} 91274462Salfred 91374462Salfred/* 91487096Salfred * delete_nfslock: delete an NFS lock list entry 91587096Salfred * 91687096Salfred * This routine is used to delete a lock out of the NFS lock list 91787096Salfred * without regard to status, underlying locks, regions or anything else 91887096Salfred * 91987096Salfred * Note that this routine *does not deallocate memory* of the lock. 92087096Salfred * It just disconnects it from the list. The lock can then be used 92187096Salfred * by other routines without fear of trashing the list. 92274462Salfred */ 92387096Salfred 92487096Salfredenum nfslock_status 92587096Salfreddelete_nfslock(struct file_lock *fl) 92674462Salfred{ 92774462Salfred 92887096Salfred LIST_REMOVE(fl, nfslocklist); 92987096Salfred 93087096Salfred return (NFS_GRANTED); 93187096Salfred} 93287096Salfred 93387096Salfredenum split_status 93487096Salfredsplit_nfslock(exist_lock, unlock_lock, left_lock, right_lock) 93587096Salfred const struct file_lock *exist_lock, *unlock_lock; 93687096Salfred struct file_lock **left_lock, **right_lock; 93787096Salfred{ 93887096Salfred u_int64_t start1, len1, start2, len2; 93987096Salfred enum split_status spstatus; 94087096Salfred 94187096Salfred spstatus = region_compare(exist_lock->client.l_offset, exist_lock->client.l_len, 94287096Salfred unlock_lock->client.l_offset, unlock_lock->client.l_len, 94387096Salfred &start1, &len1, &start2, &len2); 94487096Salfred 94587096Salfred if ((spstatus & SPL_LOCK1) != 0) { 946132254Smr *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie, exist_lock->addr, exist_lock->client_name); 94787096Salfred if (*left_lock == NULL) { 94887096Salfred debuglog("Unable to allocate resource for split 1\n"); 94987096Salfred return SPL_RESERR; 95087096Salfred } 95187096Salfred 95287199Salfred fill_file_lock(*left_lock, &exist_lock->filehandle, 95387096Salfred exist_lock->client.exclusive, exist_lock->client.svid, 95487096Salfred start1, len1, 955132254Smr exist_lock->nsm_status, 95687096Salfred exist_lock->status, exist_lock->flags, exist_lock->blocking); 95774462Salfred } 95887096Salfred 95987096Salfred if ((spstatus & SPL_LOCK2) != 0) { 960132254Smr *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie, exist_lock->addr, exist_lock->client_name); 96187096Salfred if (*right_lock == NULL) { 96287096Salfred debuglog("Unable to allocate resource for split 1\n"); 96387096Salfred if (*left_lock != NULL) { 96487096Salfred deallocate_file_lock(*left_lock); 96587096Salfred } 96687096Salfred return SPL_RESERR; 96787096Salfred } 96887096Salfred 96987199Salfred fill_file_lock(*right_lock, &exist_lock->filehandle, 97087096Salfred exist_lock->client.exclusive, exist_lock->client.svid, 97187096Salfred start2, len2, 972132254Smr exist_lock->nsm_status, 97387096Salfred exist_lock->status, exist_lock->flags, exist_lock->blocking); 97474462Salfred } 97587096Salfred 97687096Salfred return spstatus; 97787096Salfred} 97887096Salfred 97987096Salfredenum nfslock_status 98087096Salfredunlock_nfslock(fl, released_lock, left_lock, right_lock) 98187096Salfred const struct file_lock *fl; 98287096Salfred struct file_lock **released_lock; 98387096Salfred struct file_lock **left_lock; 98487096Salfred struct file_lock **right_lock; 98587096Salfred{ 98687096Salfred struct file_lock *mfl; /* Matching file lock */ 98787096Salfred enum nfslock_status retval; 98887096Salfred enum split_status spstatus; 98987096Salfred 99087096Salfred debuglog("Entering unlock_nfslock\n"); 99187096Salfred 99287096Salfred *released_lock = NULL; 99387096Salfred *left_lock = NULL; 99487096Salfred *right_lock = NULL; 99587096Salfred 99687096Salfred retval = NFS_DENIED_NOLOCK; 99787096Salfred 998132254Smr debuglog("Attempting to match lock...\n"); 99987096Salfred mfl = get_lock_matching_unlock(fl); 100087096Salfred 100187096Salfred if (mfl != NULL) { 100287096Salfred debuglog("Unlock matched. Querying for split\n"); 100387096Salfred 100487096Salfred spstatus = split_nfslock(mfl, fl, left_lock, right_lock); 100587096Salfred 100687096Salfred debuglog("Split returned %d %p %p %p %p\n",spstatus,mfl,fl,*left_lock,*right_lock); 100787096Salfred debuglog("********Split dumps********"); 100887096Salfred dump_filelock(mfl); 100987096Salfred dump_filelock(fl); 101087096Salfred dump_filelock(*left_lock); 101187096Salfred dump_filelock(*right_lock); 101287096Salfred debuglog("********End Split dumps********"); 101387096Salfred 101487096Salfred if (spstatus == SPL_RESERR) { 101587096Salfred if (*left_lock != NULL) { 101687096Salfred deallocate_file_lock(*left_lock); 101787096Salfred *left_lock = NULL; 101874462Salfred } 101987096Salfred 102087096Salfred if (*right_lock != NULL) { 102187096Salfred deallocate_file_lock(*right_lock); 102287096Salfred *right_lock = NULL; 102374462Salfred } 102487096Salfred 102587096Salfred return NFS_RESERR; 102674462Salfred } 102787096Salfred 102887096Salfred /* Insert new locks from split if required */ 102987096Salfred if (*left_lock != NULL) { 103087096Salfred debuglog("Split left activated\n"); 103187096Salfred LIST_INSERT_HEAD(&nfslocklist_head, *left_lock, nfslocklist); 103287096Salfred } 103387096Salfred 103487096Salfred if (*right_lock != NULL) { 103587096Salfred debuglog("Split right activated\n"); 103687096Salfred LIST_INSERT_HEAD(&nfslocklist_head, *right_lock, nfslocklist); 103787096Salfred } 103887096Salfred 103987096Salfred /* Unlock the lock since it matches identity */ 104087096Salfred LIST_REMOVE(mfl, nfslocklist); 104187096Salfred *released_lock = mfl; 104287096Salfred retval = NFS_GRANTED; 104374462Salfred } 104487096Salfred 104587096Salfred debuglog("Exiting unlock_nfslock\n"); 104687096Salfred 104774462Salfred return retval; 104874462Salfred} 104974462Salfred 105087096Salfred/* 105187096Salfred * Below here are the routines for manipulating the file lock directly 105287096Salfred * on the disk hardware itself 105387096Salfred */ 105487096Salfredenum hwlock_status 105587096Salfredlock_hwlock(struct file_lock *fl) 105674462Salfred{ 105787096Salfred struct monfile *imf,*nmf; 105887096Salfred int lflags, flerror; 105974462Salfred 106087096Salfred /* Scan to see if filehandle already present */ 106187096Salfred LIST_FOREACH(imf, &monfilelist_head, monfilelist) { 106287096Salfred if (bcmp(&fl->filehandle, &imf->filehandle, 106387096Salfred sizeof(fl->filehandle)) == 0) { 106487096Salfred /* imf is the correct filehandle */ 106574462Salfred break; 106687096Salfred } 106787096Salfred } 106887096Salfred 106987096Salfred /* 107087096Salfred * Filehandle already exists (we control the file) 107187096Salfred * *AND* NFS has already cleared the lock for availability 107287096Salfred * Grant it and bump the refcount. 107387096Salfred */ 107487096Salfred if (imf != NULL) { 107587096Salfred ++(imf->refcount); 107687096Salfred return (HW_GRANTED); 107787096Salfred } 107887096Salfred 107987096Salfred /* No filehandle found, create and go */ 108087096Salfred nmf = malloc(sizeof(struct monfile)); 108187096Salfred if (nmf == NULL) { 108287096Salfred debuglog("hwlock resource allocation failure\n"); 108387096Salfred return (HW_RESERR); 108487096Salfred } 108587096Salfred 108687096Salfred /* XXX: Is O_RDWR always the correct mode? */ 108787096Salfred nmf->fd = fhopen(&fl->filehandle, O_RDWR); 108887096Salfred if (nmf->fd < 0) { 108987096Salfred debuglog("fhopen failed (from %16s): %32s\n", 109087096Salfred fl->client_name, strerror(errno)); 109187096Salfred free(nmf); 109287096Salfred switch (errno) { 109387096Salfred case ESTALE: 109487096Salfred return (HW_STALEFH); 109587096Salfred case EROFS: 109687096Salfred return (HW_READONLY); 109787096Salfred default: 109887096Salfred return (HW_RESERR); 109987096Salfred } 110087096Salfred } 110187096Salfred 110287096Salfred /* File opened correctly, fill the monitor struct */ 110387096Salfred bcopy(&fl->filehandle, &nmf->filehandle, sizeof(fl->filehandle)); 110487096Salfred nmf->refcount = 1; 110587096Salfred nmf->exclusive = fl->client.exclusive; 110687096Salfred 110787096Salfred lflags = (nmf->exclusive == 1) ? 110887096Salfred (LOCK_EX | LOCK_NB) : (LOCK_SH | LOCK_NB); 110987096Salfred 111087096Salfred flerror = flock(nmf->fd, lflags); 111187096Salfred 111287096Salfred if (flerror != 0) { 111387096Salfred debuglog("flock failed (from %16s): %32s\n", 111487096Salfred fl->client_name, strerror(errno)); 111587096Salfred close(nmf->fd); 111687096Salfred free(nmf); 111787096Salfred switch (errno) { 111887096Salfred case EAGAIN: 111987096Salfred return (HW_DENIED); 112087096Salfred case ESTALE: 112187096Salfred return (HW_STALEFH); 112287096Salfred case EROFS: 112387096Salfred return (HW_READONLY); 112487096Salfred default: 112587096Salfred return (HW_RESERR); 112674462Salfred break; 112787096Salfred } 112887096Salfred } 112987096Salfred 113087096Salfred /* File opened and locked */ 113187096Salfred LIST_INSERT_HEAD(&monfilelist_head, nmf, monfilelist); 113287096Salfred 113387096Salfred debuglog("flock succeeded (from %16s)\n", fl->client_name); 113487096Salfred return (HW_GRANTED); 113587096Salfred} 1136165776Smjacob 113787096Salfredenum hwlock_status 113887096Salfredunlock_hwlock(const struct file_lock *fl) 113987096Salfred{ 114087096Salfred struct monfile *imf; 114187096Salfred 114287096Salfred debuglog("Entering unlock_hwlock\n"); 114387096Salfred debuglog("Entering loop interation\n"); 114487096Salfred 114587096Salfred /* Scan to see if filehandle already present */ 114687096Salfred LIST_FOREACH(imf, &monfilelist_head, monfilelist) { 114787096Salfred if (bcmp(&fl->filehandle, &imf->filehandle, 114887096Salfred sizeof(fl->filehandle)) == 0) { 114987096Salfred /* imf is the correct filehandle */ 115074462Salfred break; 115174462Salfred } 115274462Salfred } 115387096Salfred 115487096Salfred debuglog("Completed iteration. Proceeding\n"); 115587096Salfred 115687096Salfred if (imf == NULL) { 115787096Salfred /* No lock found */ 115887096Salfred debuglog("Exiting unlock_hwlock (HW_DENIED_NOLOCK)\n"); 115987096Salfred return (HW_DENIED_NOLOCK); 116087096Salfred } 116187096Salfred 116287096Salfred /* Lock found */ 116387096Salfred --imf->refcount; 116487096Salfred 116587096Salfred if (imf->refcount < 0) { 116687096Salfred debuglog("Negative hardware reference count\n"); 116787096Salfred } 116887096Salfred 116987096Salfred if (imf->refcount <= 0) { 117087096Salfred close(imf->fd); 117187096Salfred LIST_REMOVE(imf, monfilelist); 117287096Salfred free(imf); 117387096Salfred } 117487096Salfred debuglog("Exiting unlock_hwlock (HW_GRANTED)\n"); 117587096Salfred return (HW_GRANTED); 117674462Salfred} 117774462Salfred 117887096Salfredenum hwlock_status 117992911Salfredtest_hwlock(fl, conflicting_fl) 118092911Salfred const struct file_lock *fl __unused; 118192911Salfred struct file_lock **conflicting_fl __unused; 118287096Salfred{ 118387096Salfred 118487096Salfred /* 118587096Salfred * XXX: lock tests on hardware are not required until 118687096Salfred * true partial file testing is done on the underlying file 118787096Salfred */ 118887096Salfred return (HW_RESERR); 118987096Salfred} 119087096Salfred 119187096Salfred 119287096Salfred 119387096Salfred/* 119487096Salfred * Below here are routines for manipulating blocked lock requests 119587096Salfred * They should only be called from the XXX_partialfilelock routines 119687096Salfred * if at all possible 119787096Salfred */ 119887096Salfred 1199165775Smjacobint 1200165775Smjacobduplicate_block(struct file_lock *fl) 1201165775Smjacob{ 1202216603Suqs struct file_lock *ifl; 1203165775Smjacob int retval = 0; 1204165775Smjacob 1205165775Smjacob debuglog("Entering duplicate_block"); 1206165775Smjacob 1207165775Smjacob /* 1208165775Smjacob * Is this lock request already on the blocking list? 1209166054Sbrueffer * Consider it a dupe if the file handles, offset, length, 1210165775Smjacob * exclusivity and client match. 1211165775Smjacob */ 1212165775Smjacob LIST_FOREACH(ifl, &blockedlocklist_head, nfslocklist) { 1213165775Smjacob if (!bcmp(&fl->filehandle, &ifl->filehandle, 1214165775Smjacob sizeof(fhandle_t)) && 1215165775Smjacob fl->client.exclusive == ifl->client.exclusive && 1216165775Smjacob fl->client.l_offset == ifl->client.l_offset && 1217165775Smjacob fl->client.l_len == ifl->client.l_len && 1218165775Smjacob same_filelock_identity(fl, ifl)) { 1219165775Smjacob retval = 1; 1220165775Smjacob break; 1221165775Smjacob } 1222165775Smjacob } 1223165775Smjacob 1224165775Smjacob debuglog("Exiting duplicate_block: %s\n", retval ? "already blocked" 1225165775Smjacob : "not already blocked"); 1226165775Smjacob return retval; 1227165775Smjacob} 1228165775Smjacob 122974462Salfredvoid 123087096Salfredadd_blockingfilelock(struct file_lock *fl) 123174462Salfred{ 123287096Salfred debuglog("Entering add_blockingfilelock\n"); 123387096Salfred 123487096Salfred /* 1235165775Smjacob * A blocking lock request _should_ never be duplicated as a client 1236165775Smjacob * that is already blocked shouldn't be able to request another 1237165775Smjacob * lock. Alas, there are some buggy clients that do request the same 1238165775Smjacob * lock repeatedly. Make sure only unique locks are on the blocked 1239165775Smjacob * lock list. 1240165775Smjacob */ 1241165775Smjacob if (duplicate_block(fl)) { 1242165775Smjacob debuglog("Exiting add_blockingfilelock: already blocked\n"); 1243165775Smjacob return; 1244165775Smjacob } 1245165775Smjacob 1246165775Smjacob /* 124787096Salfred * Clear the blocking flag so that it can be reused without 124887096Salfred * adding it to the blocking queue a second time 124987096Salfred */ 125087096Salfred 125187096Salfred fl->blocking = 0; 125287096Salfred LIST_INSERT_HEAD(&blockedlocklist_head, fl, nfslocklist); 125387096Salfred 1254165775Smjacob debuglog("Exiting add_blockingfilelock: added blocked lock\n"); 125574462Salfred} 125674462Salfred 125774462Salfredvoid 125887096Salfredremove_blockingfilelock(struct file_lock *fl) 125974462Salfred{ 126074462Salfred 126187096Salfred debuglog("Entering remove_blockingfilelock\n"); 126287096Salfred 126387096Salfred LIST_REMOVE(fl, nfslocklist); 126487096Salfred 126587096Salfred debuglog("Exiting remove_blockingfilelock\n"); 126687096Salfred} 126787096Salfred 126887096Salfredvoid 126987096Salfredclear_blockingfilelock(const char *hostname) 127087096Salfred{ 127187096Salfred struct file_lock *ifl,*nfl; 127287096Salfred 127387096Salfred /* 127487096Salfred * Normally, LIST_FOREACH is called for, but since 127587096Salfred * the current element *is* the iterator, deleting it 127687096Salfred * would mess up the iteration. Thus, a next element 127787096Salfred * must be used explicitly 127887096Salfred */ 127987096Salfred 128087096Salfred ifl = LIST_FIRST(&blockedlocklist_head); 128187096Salfred 128287096Salfred while (ifl != NULL) { 128387096Salfred nfl = LIST_NEXT(ifl, nfslocklist); 128487096Salfred 128587096Salfred if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { 128687096Salfred remove_blockingfilelock(ifl); 128787096Salfred deallocate_file_lock(ifl); 128874462Salfred } 128987096Salfred 129087096Salfred ifl = nfl; 129187096Salfred } 129287096Salfred} 129387096Salfred 129487096Salfredvoid 129587096Salfredretry_blockingfilelocklist(void) 129687096Salfred{ 129787096Salfred /* Retry all locks in the blocked list */ 1298132254Smr struct file_lock *ifl, *nfl; /* Iterator */ 129987096Salfred enum partialfilelock_status pflstatus; 130087096Salfred 130187096Salfred debuglog("Entering retry_blockingfilelocklist\n"); 130287096Salfred 1303132254Smr LIST_FOREACH_SAFE(ifl, &blockedlocklist_head, nfslocklist, nfl) { 130487096Salfred debuglog("Iterator choice %p\n",ifl); 130587096Salfred debuglog("Next iterator choice %p\n",nfl); 130687096Salfred 130787096Salfred /* 130887096Salfred * SUBTLE BUG: The file_lock must be removed from the 130987096Salfred * old list so that it's list pointers get disconnected 131087096Salfred * before being allowed to participate in the new list 131187096Salfred * which will automatically add it in if necessary. 131287096Salfred */ 131387096Salfred 131487096Salfred LIST_REMOVE(ifl, nfslocklist); 131587096Salfred pflstatus = lock_partialfilelock(ifl); 1316165776Smjacob 131787096Salfred if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) { 131887096Salfred debuglog("Granted blocked lock\n"); 131987096Salfred /* lock granted and is now being used */ 132087096Salfred send_granted(ifl,0); 132187096Salfred } else { 1322132254Smr /* Reinsert lock back into blocked list */ 132387096Salfred debuglog("Replacing blocked lock\n"); 1324132254Smr LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist); 132574462Salfred } 132687096Salfred } 132787096Salfred 132887096Salfred debuglog("Exiting retry_blockingfilelocklist\n"); 132987096Salfred} 133087096Salfred 133187096Salfred/* 133287096Salfred * Below here are routines associated with manipulating all 133387096Salfred * aspects of the partial file locking system (list, hardware, etc.) 133487096Salfred */ 133587096Salfred 133687096Salfred/* 133787096Salfred * Please note that lock monitoring must be done at this level which 133887096Salfred * keeps track of *individual* lock requests on lock and unlock 133987096Salfred * 134087096Salfred * XXX: Split unlocking is going to make the unlock code miserable 134187096Salfred */ 134287096Salfred 134387096Salfred/* 134487096Salfred * lock_partialfilelock: 134587096Salfred * 134687096Salfred * Argument fl gets modified as its list housekeeping entries get modified 134787096Salfred * upon insertion into the NFS lock list 134887096Salfred * 134987096Salfred * This routine makes several assumptions: 135087096Salfred * 1) It (will) pass locks through to flock to lock the entire underlying file 135187096Salfred * and then parcel out NFS locks if it gets control of the file. 135287096Salfred * This matches the old rpc.lockd file semantics (except where it 135387096Salfred * is now more correct). It is the safe solution, but will cause 135487096Salfred * overly restrictive blocking if someone is trying to use the 135587096Salfred * underlying files without using NFS. This appears to be an 135687096Salfred * acceptable tradeoff since most people use standalone NFS servers. 135787096Salfred * XXX: The right solution is probably kevent combined with fcntl 135887096Salfred * 135987096Salfred * 2) Nothing modifies the lock lists between testing and granting 136087096Salfred * I have no idea whether this is a useful assumption or not 136187096Salfred */ 136287096Salfred 136387096Salfredenum partialfilelock_status 136487096Salfredlock_partialfilelock(struct file_lock *fl) 136587096Salfred{ 136687096Salfred enum partialfilelock_status retval; 136787096Salfred enum nfslock_status lnlstatus; 136887096Salfred enum hwlock_status hwstatus; 136987096Salfred 137087096Salfred debuglog("Entering lock_partialfilelock\n"); 137187096Salfred 137287096Salfred retval = PFL_DENIED; 137387096Salfred 137487096Salfred /* 137587096Salfred * Execute the NFS lock first, if possible, as it is significantly 137687096Salfred * easier and less expensive to undo than the filesystem lock 137787096Salfred */ 137887096Salfred 137987096Salfred lnlstatus = lock_nfslock(fl); 138087096Salfred 138187096Salfred switch (lnlstatus) { 138287096Salfred case NFS_GRANTED: 138387096Salfred case NFS_GRANTED_DUPLICATE: 138474462Salfred /* 138587096Salfred * At this point, the NFS lock is allocated and active. 138687096Salfred * Remember to clean it up if the hardware lock fails 138774462Salfred */ 138887096Salfred hwstatus = lock_hwlock(fl); 138987096Salfred 139087096Salfred switch (hwstatus) { 139187096Salfred case HW_GRANTED: 139287096Salfred case HW_GRANTED_DUPLICATE: 139387096Salfred debuglog("HW GRANTED\n"); 139487096Salfred /* 139587096Salfred * XXX: Fixme: Check hwstatus for duplicate when 139687096Salfred * true partial file locking and accounting is 1397165776Smjacob * done on the hardware. 139887096Salfred */ 139987096Salfred if (lnlstatus == NFS_GRANTED_DUPLICATE) { 140087096Salfred retval = PFL_GRANTED_DUPLICATE; 140187096Salfred } else { 140287096Salfred retval = PFL_GRANTED; 140387096Salfred } 140487096Salfred monitor_lock_host(fl->client_name); 140587096Salfred break; 140687096Salfred case HW_RESERR: 140787096Salfred debuglog("HW RESERR\n"); 140887096Salfred retval = PFL_HWRESERR; 140987096Salfred break; 141087096Salfred case HW_DENIED: 141187096Salfred debuglog("HW DENIED\n"); 141287096Salfred retval = PFL_HWDENIED; 141387096Salfred break; 141487096Salfred default: 141587096Salfred debuglog("Unmatched hwstatus %d\n",hwstatus); 141687096Salfred break; 141774462Salfred } 141887096Salfred 141987096Salfred if (retval != PFL_GRANTED && 142087096Salfred retval != PFL_GRANTED_DUPLICATE) { 142187096Salfred /* Clean up the NFS lock */ 142287096Salfred debuglog("Deleting trial NFS lock\n"); 142387096Salfred delete_nfslock(fl); 142487096Salfred } 142587096Salfred break; 142687096Salfred case NFS_DENIED: 142787096Salfred retval = PFL_NFSDENIED; 142887096Salfred break; 142987096Salfred case NFS_RESERR: 143087096Salfred retval = PFL_NFSRESERR; 143187096Salfred default: 143287096Salfred debuglog("Unmatched lnlstatus %d\n"); 143387096Salfred retval = PFL_NFSDENIED_NOLOCK; 143487096Salfred break; 143587096Salfred } 143687096Salfred 143787096Salfred /* 143887096Salfred * By the time fl reaches here, it is completely free again on 143987096Salfred * failure. The NFS lock done before attempting the 144087096Salfred * hardware lock has been backed out 144187096Salfred */ 144287096Salfred 144387096Salfred if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) { 144487096Salfred /* Once last chance to check the lock */ 144587096Salfred if (fl->blocking == 1) { 1446115004Srwatson if (retval == PFL_NFSDENIED) { 1447115004Srwatson /* Queue the lock */ 1448115004Srwatson debuglog("BLOCKING LOCK RECEIVED\n"); 1449115004Srwatson retval = PFL_NFSBLOCKED; 1450115004Srwatson add_blockingfilelock(fl); 1451115004Srwatson dump_filelock(fl); 1452115004Srwatson } else { 1453115004Srwatson /* retval is okay as PFL_HWDENIED */ 1454115004Srwatson debuglog("BLOCKING LOCK DENIED IN HARDWARE\n"); 1455115004Srwatson dump_filelock(fl); 1456115004Srwatson } 145774462Salfred } else { 145887096Salfred /* Leave retval alone, it's already correct */ 145987096Salfred debuglog("Lock denied. Non-blocking failure\n"); 146087096Salfred dump_filelock(fl); 146187096Salfred } 146287096Salfred } 146387096Salfred 146487096Salfred debuglog("Exiting lock_partialfilelock\n"); 146587096Salfred 146687096Salfred return retval; 146787096Salfred} 146887096Salfred 146987096Salfred/* 147087096Salfred * unlock_partialfilelock: 147187096Salfred * 147287096Salfred * Given a file_lock, unlock all locks which match. 147387096Salfred * 147487096Salfred * Note that a given lock might have to unlock ITSELF! See 147587096Salfred * clear_partialfilelock for example. 147687096Salfred */ 147787096Salfred 147887096Salfredenum partialfilelock_status 147987096Salfredunlock_partialfilelock(const struct file_lock *fl) 148087096Salfred{ 148187096Salfred struct file_lock *lfl,*rfl,*releasedfl,*selffl; 148287096Salfred enum partialfilelock_status retval; 148387096Salfred enum nfslock_status unlstatus; 148487096Salfred enum hwlock_status unlhwstatus, lhwstatus; 148587096Salfred 148687096Salfred debuglog("Entering unlock_partialfilelock\n"); 148787096Salfred 148887096Salfred selffl = NULL; 148987096Salfred lfl = NULL; 149087096Salfred rfl = NULL; 149187096Salfred releasedfl = NULL; 149287096Salfred retval = PFL_DENIED; 149387096Salfred 149487096Salfred /* 149587096Salfred * There are significant overlap and atomicity issues 149687096Salfred * with partially releasing a lock. For example, releasing 149787096Salfred * part of an NFS shared lock does *not* always release the 149887096Salfred * corresponding part of the file since there is only one 149987096Salfred * rpc.lockd UID but multiple users could be requesting it 150087096Salfred * from NFS. Also, an unlock request should never allow 150187096Salfred * another process to gain a lock on the remaining parts. 150287096Salfred * ie. Always apply the new locks before releasing the 150387096Salfred * old one 150487096Salfred */ 150587096Salfred 150687096Salfred /* 150787096Salfred * Loop is required since multiple little locks 150887096Salfred * can be allocated and then deallocated with one 150987096Salfred * big unlock. 151087096Salfred * 151187096Salfred * The loop is required to be here so that the nfs & 151287096Salfred * hw subsystems do not need to communicate with one 151387096Salfred * one another 151487096Salfred */ 151587096Salfred 151687096Salfred do { 151787096Salfred debuglog("Value of releasedfl: %p\n",releasedfl); 151887096Salfred /* lfl&rfl are created *AND* placed into the NFS lock list if required */ 151987096Salfred unlstatus = unlock_nfslock(fl, &releasedfl, &lfl, &rfl); 152087096Salfred debuglog("Value of releasedfl: %p\n",releasedfl); 152187096Salfred 152287096Salfred 152387096Salfred /* XXX: This is grungy. It should be refactored to be cleaner */ 152487096Salfred if (lfl != NULL) { 152587096Salfred lhwstatus = lock_hwlock(lfl); 152687096Salfred if (lhwstatus != HW_GRANTED && 152787096Salfred lhwstatus != HW_GRANTED_DUPLICATE) { 152887096Salfred debuglog("HW duplicate lock failure for left split\n"); 152974462Salfred } 153087096Salfred monitor_lock_host(lfl->client_name); 153187096Salfred } 153287096Salfred 153387096Salfred if (rfl != NULL) { 153487096Salfred lhwstatus = lock_hwlock(rfl); 153587096Salfred if (lhwstatus != HW_GRANTED && 153687096Salfred lhwstatus != HW_GRANTED_DUPLICATE) { 153787096Salfred debuglog("HW duplicate lock failure for right split\n"); 153887096Salfred } 153987096Salfred monitor_lock_host(rfl->client_name); 154087096Salfred } 154187096Salfred 154287096Salfred switch (unlstatus) { 154387096Salfred case NFS_GRANTED: 154487096Salfred /* Attempt to unlock on the hardware */ 154587096Salfred debuglog("NFS unlock granted. Attempting hardware unlock\n"); 154687096Salfred 154787096Salfred /* This call *MUST NOT* unlock the two newly allocated locks */ 154887096Salfred unlhwstatus = unlock_hwlock(fl); 154987096Salfred debuglog("HW unlock returned with code %d\n",unlhwstatus); 1550165776Smjacob 155187096Salfred switch (unlhwstatus) { 155287096Salfred case HW_GRANTED: 155387096Salfred debuglog("HW unlock granted\n"); 155487096Salfred unmonitor_lock_host(releasedfl->client_name); 155587096Salfred retval = PFL_GRANTED; 155674462Salfred break; 155787096Salfred case HW_DENIED_NOLOCK: 155887096Salfred /* Huh?!?! This shouldn't happen */ 155987096Salfred debuglog("HW unlock denied no lock\n"); 156087096Salfred retval = PFL_HWRESERR; 156187096Salfred /* Break out of do-while */ 156287096Salfred unlstatus = NFS_RESERR; 156374462Salfred break; 156474462Salfred default: 156587096Salfred debuglog("HW unlock failed\n"); 156687096Salfred retval = PFL_HWRESERR; 156787096Salfred /* Break out of do-while */ 156887096Salfred unlstatus = NFS_RESERR; 156987096Salfred break; 157074462Salfred } 1571165776Smjacob 157287096Salfred debuglog("Exiting with status retval: %d\n",retval); 1573165776Smjacob 157487096Salfred retry_blockingfilelocklist(); 157587096Salfred break; 157687096Salfred case NFS_DENIED_NOLOCK: 157787096Salfred retval = PFL_GRANTED; 157887096Salfred debuglog("All locks cleaned out\n"); 157987096Salfred break; 158087096Salfred default: 158187096Salfred retval = PFL_NFSRESERR; 158287096Salfred debuglog("NFS unlock failure\n"); 158387096Salfred dump_filelock(fl); 158487096Salfred break; 158574462Salfred } 158687096Salfred 158787096Salfred if (releasedfl != NULL) { 158887096Salfred if (fl == releasedfl) { 158987096Salfred /* 159087096Salfred * XXX: YECHHH!!! Attempt to unlock self succeeded 159187096Salfred * but we can't deallocate the space yet. This is what 159287096Salfred * happens when you don't write malloc and free together 159387096Salfred */ 159487096Salfred debuglog("Attempt to unlock self\n"); 159587096Salfred selffl = releasedfl; 159687096Salfred } else { 159787096Salfred /* 159887096Salfred * XXX: this deallocation *still* needs to migrate closer 159987096Salfred * to the allocation code way up in get_lock or the allocation 160087096Salfred * code needs to migrate down (violation of "When you write 160187096Salfred * malloc you must write free") 160287096Salfred */ 1603165776Smjacob 160487096Salfred deallocate_file_lock(releasedfl); 160587096Salfred } 160687096Salfred } 160787096Salfred 160887096Salfred } while (unlstatus == NFS_GRANTED); 160987096Salfred 161087096Salfred if (selffl != NULL) { 161187096Salfred /* 161287096Salfred * This statement wipes out the incoming file lock (fl) 161387096Salfred * in spite of the fact that it is declared const 161487096Salfred */ 161587096Salfred debuglog("WARNING! Destroying incoming lock pointer\n"); 161687096Salfred deallocate_file_lock(selffl); 161774462Salfred } 161887096Salfred 161987096Salfred debuglog("Exiting unlock_partialfilelock\n"); 162087096Salfred 162187096Salfred return retval; 162274462Salfred} 162374462Salfred 162474462Salfred/* 162587096Salfred * clear_partialfilelock 162674462Salfred * 162787096Salfred * Normally called in response to statd state number change. 162887096Salfred * Wipe out all locks held by a host. As a bonus, the act of 162987096Salfred * doing so should automatically clear their statd entries and 163087096Salfred * unmonitor the host. 163174462Salfred */ 163274462Salfred 1633165776Smjacobvoid 163487096Salfredclear_partialfilelock(const char *hostname) 163587096Salfred{ 163687096Salfred struct file_lock *ifl, *nfl; 163787096Salfred 163887096Salfred /* Clear blocking file lock list */ 163987096Salfred clear_blockingfilelock(hostname); 164087096Salfred 164187096Salfred /* do all required unlocks */ 164287096Salfred /* Note that unlock can smash the current pointer to a lock */ 164387096Salfred 164487096Salfred /* 164587096Salfred * Normally, LIST_FOREACH is called for, but since 164687096Salfred * the current element *is* the iterator, deleting it 164787096Salfred * would mess up the iteration. Thus, a next element 164887096Salfred * must be used explicitly 164987096Salfred */ 165087096Salfred 165187096Salfred ifl = LIST_FIRST(&nfslocklist_head); 165287096Salfred 165387096Salfred while (ifl != NULL) { 165487096Salfred nfl = LIST_NEXT(ifl, nfslocklist); 165587096Salfred 165687096Salfred if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { 165787096Salfred /* Unlock destroys ifl out from underneath */ 165887096Salfred unlock_partialfilelock(ifl); 165987096Salfred /* ifl is NO LONGER VALID AT THIS POINT */ 166087096Salfred } 166187096Salfred ifl = nfl; 166287096Salfred } 166387096Salfred} 166487096Salfred 166587096Salfred/* 166687096Salfred * test_partialfilelock: 166787096Salfred */ 166887096Salfredenum partialfilelock_status 166987096Salfredtest_partialfilelock(const struct file_lock *fl, 167087096Salfred struct file_lock **conflicting_fl) 167187096Salfred{ 167287096Salfred enum partialfilelock_status retval; 167387096Salfred enum nfslock_status teststatus; 167487096Salfred 167587096Salfred debuglog("Entering testpartialfilelock...\n"); 167687096Salfred 167787096Salfred retval = PFL_DENIED; 167887096Salfred 167987096Salfred teststatus = test_nfslock(fl, conflicting_fl); 168087096Salfred debuglog("test_partialfilelock: teststatus %d\n",teststatus); 168187096Salfred 168287096Salfred if (teststatus == NFS_GRANTED || teststatus == NFS_GRANTED_DUPLICATE) { 168387096Salfred /* XXX: Add the underlying filesystem locking code */ 168487096Salfred retval = (teststatus == NFS_GRANTED) ? 168587096Salfred PFL_GRANTED : PFL_GRANTED_DUPLICATE; 168687096Salfred debuglog("Dumping locks...\n"); 168787096Salfred dump_filelock(fl); 168887096Salfred dump_filelock(*conflicting_fl); 168987096Salfred debuglog("Done dumping locks...\n"); 169087096Salfred } else { 169187096Salfred retval = PFL_NFSDENIED; 169287096Salfred debuglog("NFS test denied.\n"); 169387096Salfred dump_filelock(fl); 169487096Salfred debuglog("Conflicting.\n"); 169587096Salfred dump_filelock(*conflicting_fl); 169687096Salfred } 169787096Salfred 169887096Salfred debuglog("Exiting testpartialfilelock...\n"); 169987096Salfred 170087096Salfred return retval; 170187096Salfred} 170287096Salfred 170387096Salfred/* 170487096Salfred * Below here are routines associated with translating the partial file locking 170587096Salfred * codes into useful codes to send back to the NFS RPC messaging system 170687096Salfred */ 170787096Salfred 170887096Salfred/* 170987096Salfred * These routines translate the (relatively) useful return codes back onto 171087096Salfred * the few return codes which the nlm subsystems wishes to trasmit 171187096Salfred */ 171287096Salfred 171374462Salfredenum nlm_stats 171487096Salfreddo_test(struct file_lock *fl, struct file_lock **conflicting_fl) 171574462Salfred{ 171687096Salfred enum partialfilelock_status pfsret; 171787096Salfred enum nlm_stats retval; 171874462Salfred 171987096Salfred debuglog("Entering do_test...\n"); 172087096Salfred 172187096Salfred pfsret = test_partialfilelock(fl,conflicting_fl); 172287096Salfred 172387096Salfred switch (pfsret) { 172487096Salfred case PFL_GRANTED: 172587096Salfred debuglog("PFL test lock granted\n"); 172687096Salfred dump_filelock(fl); 172787096Salfred dump_filelock(*conflicting_fl); 172887096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 172987096Salfred break; 173087096Salfred case PFL_GRANTED_DUPLICATE: 173187096Salfred debuglog("PFL test lock granted--duplicate id detected\n"); 173287096Salfred dump_filelock(fl); 173387096Salfred dump_filelock(*conflicting_fl); 173487096Salfred debuglog("Clearing conflicting_fl for call semantics\n"); 173587096Salfred *conflicting_fl = NULL; 173687096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 173787096Salfred break; 173887096Salfred case PFL_NFSDENIED: 173987096Salfred case PFL_HWDENIED: 174087096Salfred debuglog("PFL test lock denied\n"); 174187096Salfred dump_filelock(fl); 174287096Salfred dump_filelock(*conflicting_fl); 174387096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; 174487096Salfred break; 174587096Salfred case PFL_NFSRESERR: 174687096Salfred case PFL_HWRESERR: 174787096Salfred debuglog("PFL test lock resource fail\n"); 174887096Salfred dump_filelock(fl); 174987096Salfred dump_filelock(*conflicting_fl); 175087096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; 175187096Salfred break; 175287096Salfred default: 175387096Salfred debuglog("PFL test lock *FAILED*\n"); 175487096Salfred dump_filelock(fl); 175587096Salfred dump_filelock(*conflicting_fl); 175687096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; 175787096Salfred break; 175887096Salfred } 175987096Salfred 176087096Salfred debuglog("Exiting do_test...\n"); 176187096Salfred 176287096Salfred return retval; 176387096Salfred} 176487096Salfred 176587096Salfred/* 176687096Salfred * do_lock: Try to acquire a lock 176787096Salfred * 176887096Salfred * This routine makes a distinction between NLM versions. I am pretty 176987096Salfred * convinced that this should be abstracted out and bounced up a level 177087096Salfred */ 177187096Salfred 177287096Salfredenum nlm_stats 177387096Salfreddo_lock(struct file_lock *fl) 177487096Salfred{ 177587096Salfred enum partialfilelock_status pfsret; 177687096Salfred enum nlm_stats retval; 177787096Salfred 177887096Salfred debuglog("Entering do_lock...\n"); 177987096Salfred 178087096Salfred pfsret = lock_partialfilelock(fl); 178187096Salfred 178287096Salfred switch (pfsret) { 178387096Salfred case PFL_GRANTED: 178487096Salfred debuglog("PFL lock granted"); 178587096Salfred dump_filelock(fl); 178687096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 178787096Salfred break; 178887096Salfred case PFL_GRANTED_DUPLICATE: 178987096Salfred debuglog("PFL lock granted--duplicate id detected"); 179087096Salfred dump_filelock(fl); 179187096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 179287096Salfred break; 179387096Salfred case PFL_NFSDENIED: 179487096Salfred case PFL_HWDENIED: 179587096Salfred debuglog("PFL_NFS lock denied"); 179687096Salfred dump_filelock(fl); 179787096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; 179887096Salfred break; 179987096Salfred case PFL_NFSBLOCKED: 180087096Salfred case PFL_HWBLOCKED: 180187096Salfred debuglog("PFL_NFS blocking lock denied. Queued.\n"); 180287096Salfred dump_filelock(fl); 180387096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_blocked : nlm_blocked; 180487096Salfred break; 180587096Salfred case PFL_NFSRESERR: 180687096Salfred case PFL_HWRESERR: 180787096Salfred debuglog("PFL lock resource alocation fail\n"); 180887096Salfred dump_filelock(fl); 180987096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; 181087096Salfred break; 181187096Salfred default: 181287096Salfred debuglog("PFL lock *FAILED*"); 181387096Salfred dump_filelock(fl); 181487096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; 181587096Salfred break; 181687096Salfred } 181787096Salfred 181887096Salfred debuglog("Exiting do_lock...\n"); 181987096Salfred 182087096Salfred return retval; 182187096Salfred} 182287096Salfred 182387096Salfredenum nlm_stats 182487096Salfreddo_unlock(struct file_lock *fl) 182587096Salfred{ 182687096Salfred enum partialfilelock_status pfsret; 182787096Salfred enum nlm_stats retval; 182887096Salfred 182987096Salfred debuglog("Entering do_unlock...\n"); 183087096Salfred pfsret = unlock_partialfilelock(fl); 183187096Salfred 183287096Salfred switch (pfsret) { 183387096Salfred case PFL_GRANTED: 183487096Salfred debuglog("PFL unlock granted"); 183587096Salfred dump_filelock(fl); 183687096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 183787096Salfred break; 183887096Salfred case PFL_NFSDENIED: 183987096Salfred case PFL_HWDENIED: 184087096Salfred debuglog("PFL_NFS unlock denied"); 184187096Salfred dump_filelock(fl); 184287096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; 184387096Salfred break; 184487096Salfred case PFL_NFSDENIED_NOLOCK: 184587096Salfred case PFL_HWDENIED_NOLOCK: 184687096Salfred debuglog("PFL_NFS no lock found\n"); 184787096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 184887096Salfred break; 184987096Salfred case PFL_NFSRESERR: 185087096Salfred case PFL_HWRESERR: 185187096Salfred debuglog("PFL unlock resource failure"); 185287096Salfred dump_filelock(fl); 185387096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; 185487096Salfred break; 185587096Salfred default: 185687096Salfred debuglog("PFL unlock *FAILED*"); 185787096Salfred dump_filelock(fl); 185887096Salfred retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; 185987096Salfred break; 186087096Salfred } 186187096Salfred 186287096Salfred debuglog("Exiting do_unlock...\n"); 186387096Salfred 186487096Salfred return retval; 186587096Salfred} 186687096Salfred 186787096Salfred/* 186887096Salfred * do_clear 186987096Salfred * 187087096Salfred * This routine is non-existent because it doesn't have a return code. 187187096Salfred * It is here for completeness in case someone *does* need to do return 187287096Salfred * codes later. A decent compiler should optimize this away. 187387096Salfred */ 187487096Salfred 187587096Salfredvoid 187687096Salfreddo_clear(const char *hostname) 187787096Salfred{ 187887096Salfred 187987096Salfred clear_partialfilelock(hostname); 188087096Salfred} 188187096Salfred 188287096Salfred/* 188387096Salfred * The following routines are all called from the code which the 188487096Salfred * RPC layer invokes 188587096Salfred */ 188687096Salfred 188787096Salfred/* 188887096Salfred * testlock(): inform the caller if the requested lock would be granted 188987096Salfred * 189087096Salfred * returns NULL if lock would granted 189187096Salfred * returns pointer to a conflicting nlm4_holder if not 189287096Salfred */ 189387096Salfred 189487096Salfredstruct nlm4_holder * 189592911Salfredtestlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused) 189687096Salfred{ 189787096Salfred struct file_lock test_fl, *conflicting_fl; 189887096Salfred 189987096Salfred bzero(&test_fl, sizeof(test_fl)); 190087096Salfred 190187096Salfred bcopy(lock->fh.n_bytes, &(test_fl.filehandle), sizeof(fhandle_t)); 190287096Salfred copy_nlm4_lock_to_nlm4_holder(lock, exclusive, &test_fl.client); 190387096Salfred 190487096Salfred siglock(); 190587096Salfred do_test(&test_fl, &conflicting_fl); 1906165776Smjacob 190787096Salfred if (conflicting_fl == NULL) { 190887096Salfred debuglog("No conflicting lock found\n"); 190987096Salfred sigunlock(); 191087096Salfred return NULL; 191187096Salfred } else { 191287096Salfred debuglog("Found conflicting lock\n"); 191387096Salfred dump_filelock(conflicting_fl); 191487096Salfred sigunlock(); 191587096Salfred return (&conflicting_fl->client); 191687096Salfred } 191787096Salfred} 191887096Salfred 191987096Salfred/* 1920165776Smjacob * getlock: try to aquire the lock. 192187096Salfred * If file is already locked and we can sleep, put the lock in the list with 192287096Salfred * status LKST_WAITING; it'll be processed later. 192387096Salfred * Otherwise try to lock. If we're allowed to block, fork a child which 192487096Salfred * will do the blocking lock. 192587096Salfred */ 192687096Salfred 192787096Salfredenum nlm_stats 192887096Salfredgetlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags) 192987096Salfred{ 193087096Salfred struct file_lock *newfl; 193187096Salfred enum nlm_stats retval; 193287096Salfred 193387096Salfred debuglog("Entering getlock...\n"); 193487096Salfred 193587096Salfred if (grace_expired == 0 && lckarg->reclaim == 0) 193687096Salfred return (flags & LOCK_V4) ? 193787096Salfred nlm4_denied_grace_period : nlm_denied_grace_period; 193887096Salfred 193987096Salfred /* allocate new file_lock for this request */ 1940132254Smr newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->cookie, 1941132254Smr (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf, lckarg->alock.caller_name); 194287096Salfred if (newfl == NULL) { 194387096Salfred syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno)); 194487096Salfred /* failed */ 194587096Salfred return (flags & LOCK_V4) ? 194687096Salfred nlm4_denied_nolocks : nlm_denied_nolocks; 194787096Salfred } 194887096Salfred 194987096Salfred if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) { 195087096Salfred debuglog("recieved fhandle size %d, local size %d", 195187096Salfred lckarg->alock.fh.n_len, (int)sizeof(fhandle_t)); 195287096Salfred } 195387096Salfred 195487096Salfred fill_file_lock(newfl, (fhandle_t *)lckarg->alock.fh.n_bytes, 195587199Salfred lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset, 195687199Salfred lckarg->alock.l_len, 1957132254Smr lckarg->state, 0, flags, lckarg->block); 1958165776Smjacob 195987096Salfred /* 196087096Salfred * newfl is now fully constructed and deallocate_file_lock 196187096Salfred * can now be used to delete it 196287096Salfred */ 1963165776Smjacob 196487096Salfred siglock(); 196587096Salfred debuglog("Pointer to new lock is %p\n",newfl); 196687096Salfred 196787096Salfred retval = do_lock(newfl); 196887096Salfred 196987096Salfred debuglog("Pointer to new lock is %p\n",newfl); 197087096Salfred sigunlock(); 1971165776Smjacob 197287096Salfred switch (retval) 197387096Salfred { 197487096Salfred case nlm4_granted: 197587096Salfred /* case nlm_granted: is the same as nlm4_granted */ 197687096Salfred /* do_mon(lckarg->alock.caller_name); */ 197774462Salfred break; 197887096Salfred case nlm4_blocked: 197987096Salfred /* case nlm_blocked: is the same as nlm4_blocked */ 198087096Salfred /* do_mon(lckarg->alock.caller_name); */ 198174462Salfred break; 198274462Salfred default: 198387096Salfred deallocate_file_lock(newfl); 198487096Salfred break; 198574462Salfred } 198687096Salfred 198787096Salfred debuglog("Exiting getlock...\n"); 198887096Salfred 198987096Salfred return retval; 199087096Salfred} 199187096Salfred 199287096Salfred 199387096Salfred/* unlock a filehandle */ 199487096Salfredenum nlm_stats 199592911Salfredunlock(nlm4_lock *lock, const int flags __unused) 199687096Salfred{ 199787096Salfred struct file_lock fl; 199887096Salfred enum nlm_stats err; 1999165776Smjacob 200087096Salfred siglock(); 2001165776Smjacob 200287096Salfred debuglog("Entering unlock...\n"); 2003165776Smjacob 200487096Salfred bzero(&fl,sizeof(struct file_lock)); 200587096Salfred bcopy(lock->fh.n_bytes, &fl.filehandle, sizeof(fhandle_t)); 2006165776Smjacob 200787096Salfred copy_nlm4_lock_to_nlm4_holder(lock, 0, &fl.client); 2008165776Smjacob 200987096Salfred err = do_unlock(&fl); 2010165776Smjacob 201187096Salfred sigunlock(); 2012165776Smjacob 201387096Salfred debuglog("Exiting unlock...\n"); 2014165776Smjacob 201587096Salfred return err; 201687096Salfred} 201787096Salfred 201887096Salfred/* 2019165776Smjacob * XXX: The following monitor/unmonitor routines 202087096Salfred * have not been extensively tested (ie. no regression 202187096Salfred * script exists like for the locking sections 202287096Salfred */ 202387096Salfred 202487096Salfred/* 202587096Salfred * monitor_lock_host: monitor lock hosts locally with a ref count and 202687096Salfred * inform statd 202787096Salfred */ 202887096Salfredvoid 202987096Salfredmonitor_lock_host(const char *hostname) 203087096Salfred{ 203187096Salfred struct host *ihp, *nhp; 203287096Salfred struct mon smon; 203387096Salfred struct sm_stat_res sres; 203487096Salfred int rpcret, statflag; 2035132254Smr size_t n; 2036165776Smjacob 203787096Salfred rpcret = 0; 203887096Salfred statflag = 0; 203987096Salfred 204087096Salfred LIST_FOREACH(ihp, &hostlst_head, hostlst) { 204187096Salfred if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) { 204287096Salfred /* Host is already monitored, bump refcount */ 204387096Salfred ++ihp->refcnt; 204487096Salfred /* Host should only be in the monitor list once */ 204587096Salfred return; 204687096Salfred } 204774462Salfred } 204887096Salfred 204987096Salfred /* Host is not yet monitored, add it */ 2050132254Smr n = strnlen(hostname, SM_MAXSTRLEN); 2051132254Smr if (n == SM_MAXSTRLEN) { 2052132254Smr return; 2053132254Smr } 2054132254Smr nhp = malloc(sizeof(*nhp) - sizeof(nhp->name) + n + 1); 205587096Salfred if (nhp == NULL) { 205687096Salfred debuglog("Unable to allocate entry for statd mon\n"); 205787096Salfred return; 205874462Salfred } 205987096Salfred 206087096Salfred /* Allocated new host entry, now fill the fields */ 2061132254Smr memcpy(nhp->name, hostname, n); 2062132254Smr nhp->name[n] = 0; 206387096Salfred nhp->refcnt = 1; 206487096Salfred debuglog("Locally Monitoring host %16s\n",hostname); 2065165776Smjacob 206687096Salfred debuglog("Attempting to tell statd\n"); 2067165776Smjacob 206887096Salfred bzero(&smon,sizeof(smon)); 2069165776Smjacob 207087096Salfred smon.mon_id.mon_name = nhp->name; 2071161231Sthomas smon.mon_id.my_id.my_name = "localhost"; 207287096Salfred smon.mon_id.my_id.my_prog = NLM_PROG; 207387096Salfred smon.mon_id.my_id.my_vers = NLM_SM; 207487096Salfred smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; 2075165776Smjacob 2076121558Speter rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_MON, 2077121558Speter (xdrproc_t)xdr_mon, &smon, 2078121558Speter (xdrproc_t)xdr_sm_stat_res, &sres); 2079165776Smjacob 208087096Salfred if (rpcret == 0) { 208187096Salfred if (sres.res_stat == stat_fail) { 208287096Salfred debuglog("Statd call failed\n"); 208387096Salfred statflag = 0; 208487096Salfred } else { 208587096Salfred statflag = 1; 208674462Salfred } 208787096Salfred } else { 208887096Salfred debuglog("Rpc call to statd failed with return value: %d\n", 208987096Salfred rpcret); 209087096Salfred statflag = 0; 209174462Salfred } 2092165776Smjacob 209387096Salfred if (statflag == 1) { 209487096Salfred LIST_INSERT_HEAD(&hostlst_head, nhp, hostlst); 209587096Salfred } else { 209687096Salfred free(nhp); 209787096Salfred } 209887096Salfred 209987096Salfred} 210087096Salfred 210187096Salfred/* 210287096Salfred * unmonitor_lock_host: clear monitor ref counts and inform statd when gone 210387096Salfred */ 210487096Salfredvoid 2105121558Speterunmonitor_lock_host(char *hostname) 210687096Salfred{ 210787096Salfred struct host *ihp; 210887096Salfred struct mon_id smon_id; 210987096Salfred struct sm_stat smstat; 211087096Salfred int rpcret; 2111165776Smjacob 211287096Salfred rpcret = 0; 211387096Salfred 211487096Salfred for( ihp=LIST_FIRST(&hostlst_head); ihp != NULL; 211587096Salfred ihp=LIST_NEXT(ihp, hostlst)) { 211687096Salfred if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) { 211787096Salfred /* Host is monitored, bump refcount */ 211887096Salfred --ihp->refcnt; 211987096Salfred /* Host should only be in the monitor list once */ 212074462Salfred break; 212174462Salfred } 212274462Salfred } 212387096Salfred 212487096Salfred if (ihp == NULL) { 212587096Salfred debuglog("Could not find host %16s in mon list\n", hostname); 212687096Salfred return; 212787096Salfred } 212887096Salfred 212987096Salfred if (ihp->refcnt > 0) 213087096Salfred return; 213187096Salfred 213287096Salfred if (ihp->refcnt < 0) { 213387096Salfred debuglog("Negative refcount!: %d\n", 213487096Salfred ihp->refcnt); 213587096Salfred } 213687096Salfred 213787096Salfred debuglog("Attempting to unmonitor host %16s\n", hostname); 213887096Salfred 213987096Salfred bzero(&smon_id,sizeof(smon_id)); 214087096Salfred 214192975Salfred smon_id.mon_name = hostname; 214287096Salfred smon_id.my_id.my_name = "localhost"; 214387096Salfred smon_id.my_id.my_prog = NLM_PROG; 214487096Salfred smon_id.my_id.my_vers = NLM_SM; 214587096Salfred smon_id.my_id.my_proc = NLM_SM_NOTIFY; 2146165776Smjacob 2147121558Speter rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON, 2148125903Salfred (xdrproc_t)xdr_mon_id, &smon_id, 2149125903Salfred (xdrproc_t)xdr_sm_stat, &smstat); 2150165776Smjacob 215187096Salfred if (rpcret != 0) { 215287096Salfred debuglog("Rpc call to unmonitor statd failed with " 215387096Salfred " return value: %d\n", rpcret); 215487096Salfred } 215587096Salfred 215687096Salfred LIST_REMOVE(ihp, hostlst); 215787096Salfred free(ihp); 215874462Salfred} 215974462Salfred 216087096Salfred/* 216187096Salfred * notify: Clear all locks from a host if statd complains 216287096Salfred * 216387096Salfred * XXX: This routine has not been thoroughly tested. However, neither 216487096Salfred * had the old one been. It used to compare the statd crash state counter 216587096Salfred * to the current lock state. The upshot of this was that it basically 216687096Salfred * cleared all locks from the specified host 99% of the time (with the 216787096Salfred * other 1% being a bug). Consequently, the assumption is that clearing 216887096Salfred * all locks from a host when notified by statd is acceptable. 216987096Salfred * 217087096Salfred * Please note that this routine skips the usual level of redirection 217187096Salfred * through a do_* type routine. This introduces a possible level of 217287096Salfred * error and might better be written as do_notify and take this one out. 217387096Salfred 217487096Salfred */ 217587096Salfred 217674462Salfredvoid 217787096Salfrednotify(const char *hostname, const int state) 217887096Salfred{ 217987096Salfred debuglog("notify from %s, new state %d", hostname, state); 2180165776Smjacob 218187096Salfred siglock(); 218287096Salfred do_clear(hostname); 218387096Salfred sigunlock(); 218487096Salfred 218587096Salfred debuglog("Leaving notify\n"); 218687096Salfred} 218787096Salfred 218887096Salfredvoid 218974462Salfredsend_granted(fl, opcode) 219074462Salfred struct file_lock *fl; 219192911Salfred int opcode __unused; 219274462Salfred{ 219374462Salfred CLIENT *cli; 219474462Salfred static char dummy; 219574462Salfred struct timeval timeo; 219674462Salfred int success; 219774462Salfred static struct nlm_res retval; 219874462Salfred static struct nlm4_res retval4; 219974462Salfred 220087096Salfred debuglog("About to send granted on blocked lock\n"); 220187096Salfred 220274462Salfred cli = get_client(fl->addr, 220374462Salfred (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); 220474462Salfred if (cli == NULL) { 220574462Salfred syslog(LOG_NOTICE, "failed to get CLIENT for %s", 220674462Salfred fl->client_name); 220774462Salfred /* 220874462Salfred * We fail to notify remote that the lock has been granted. 220974462Salfred * The client will timeout and retry, the lock will be 221074462Salfred * granted at this time. 221174462Salfred */ 221274462Salfred return; 221374462Salfred } 221474462Salfred timeo.tv_sec = 0; 221574462Salfred timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ 221674462Salfred 221774462Salfred if (fl->flags & LOCK_V4) { 221874462Salfred static nlm4_testargs res; 221974462Salfred res.cookie = fl->client_cookie; 222074462Salfred res.exclusive = fl->client.exclusive; 222174462Salfred res.alock.caller_name = fl->client_name; 222274462Salfred res.alock.fh.n_len = sizeof(fhandle_t); 222374462Salfred res.alock.fh.n_bytes = (char*)&fl->filehandle; 222474462Salfred res.alock.oh = fl->client.oh; 222574462Salfred res.alock.svid = fl->client.svid; 222674462Salfred res.alock.l_offset = fl->client.l_offset; 222774462Salfred res.alock.l_len = fl->client.l_len; 222887096Salfred debuglog("sending v4 reply%s", 222987096Salfred (fl->flags & LOCK_ASYNC) ? " (async)":""); 223074462Salfred if (fl->flags & LOCK_ASYNC) { 223174462Salfred success = clnt_call(cli, NLM4_GRANTED_MSG, 2232121558Speter (xdrproc_t)xdr_nlm4_testargs, &res, 2233121558Speter (xdrproc_t)xdr_void, &dummy, timeo); 223474462Salfred } else { 223574462Salfred success = clnt_call(cli, NLM4_GRANTED, 2236121558Speter (xdrproc_t)xdr_nlm4_testargs, &res, 2237121558Speter (xdrproc_t)xdr_nlm4_res, &retval4, timeo); 223874462Salfred } 223974462Salfred } else { 224074462Salfred static nlm_testargs res; 224174462Salfred 224274462Salfred res.cookie = fl->client_cookie; 224374462Salfred res.exclusive = fl->client.exclusive; 224474462Salfred res.alock.caller_name = fl->client_name; 224574462Salfred res.alock.fh.n_len = sizeof(fhandle_t); 224674462Salfred res.alock.fh.n_bytes = (char*)&fl->filehandle; 224774462Salfred res.alock.oh = fl->client.oh; 224874462Salfred res.alock.svid = fl->client.svid; 224974462Salfred res.alock.l_offset = fl->client.l_offset; 225074462Salfred res.alock.l_len = fl->client.l_len; 225187096Salfred debuglog("sending v1 reply%s", 225287096Salfred (fl->flags & LOCK_ASYNC) ? " (async)":""); 225374462Salfred if (fl->flags & LOCK_ASYNC) { 225474462Salfred success = clnt_call(cli, NLM_GRANTED_MSG, 2255121558Speter (xdrproc_t)xdr_nlm_testargs, &res, 2256121558Speter (xdrproc_t)xdr_void, &dummy, timeo); 225774462Salfred } else { 225874462Salfred success = clnt_call(cli, NLM_GRANTED, 2259121558Speter (xdrproc_t)xdr_nlm_testargs, &res, 2260121558Speter (xdrproc_t)xdr_nlm_res, &retval, timeo); 226174462Salfred } 226274462Salfred } 226374462Salfred if (debug_level > 2) 226487096Salfred debuglog("clnt_call returns %d(%s) for granted", 226587096Salfred success, clnt_sperrno(success)); 226674462Salfred 226774462Salfred} 226874462Salfred 226987096Salfred/* 227087096Salfred * Routines below here have not been modified in the overhaul 227187096Salfred */ 227274462Salfred 227387096Salfred/* 227487096Salfred * Are these two routines still required since lockd is not spawning off 227587096Salfred * children to service locks anymore? Presumably they were originally 227687096Salfred * put in place to prevent a one child from changing the lock list out 227787096Salfred * from under another one. 227887096Salfred */ 227974462Salfred 228074462Salfredvoid 228187096Salfredsiglock(void) 228274462Salfred{ 228387096Salfred sigset_t block; 2284165776Smjacob 228587096Salfred sigemptyset(&block); 228687096Salfred sigaddset(&block, SIGCHLD); 228774462Salfred 228887096Salfred if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { 228987096Salfred syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); 229087096Salfred } 229174462Salfred} 229274462Salfred 229374462Salfredvoid 229487096Salfredsigunlock(void) 229574462Salfred{ 229687096Salfred sigset_t block; 2297165776Smjacob 229887096Salfred sigemptyset(&block); 229987096Salfred sigaddset(&block, SIGCHLD); 2300165776Smjacob 230187096Salfred if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { 230287096Salfred syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); 230387096Salfred } 230474462Salfred} 2305