network_cmds-245.tar.gz
[apple/network_cmds.git] / rpc_lockd.tproj / lockd_lock.c
index 29a77c56f636ae9d03e3da111566de00d7d8b24a..1ee60d128510b161de10f2f8d2423f4dc461e5c8 100644 (file)
 #define MAXOBJECTSIZE 64
 #define MAXBUFFERSIZE 1024
 
-/*
- * SM_MAXSTRLEN is usually 1024.  This means that lock requests and
- * host name monitoring entries are *MUCH* larger than they should be
- */
-
 /*
  * A set of utilities for managing file locking
  *
@@ -83,13 +78,11 @@ struct file_lock {
        struct sockaddr *addr;
        struct nlm4_holder client; /* lock holder */
        u_int64_t granted_cookie;
-       char client_name[SM_MAXSTRLEN];
        int nsm_status; /* status from the remote lock manager */
        int status; /* lock status, see below */
        int flags; /* lock flags, see lockd_lock.h */
        int blocking; /* blocking lock or not */
-       pid_t locker; /* pid of the child process trying to get the lock */
-       int fd; /* file descriptor for this lock */
+       char client_name[SM_MAXSTRLEN]; /* client_name is really variable length and must be last! */
 };
 
 LIST_HEAD(nfslocklist_head, file_lock);
@@ -102,9 +95,9 @@ struct blockedlocklist_head blockedlocklist_head = LIST_HEAD_INITIALIZER(blocked
 struct file_share {
        LIST_ENTRY(file_share) nfssharelist;
        netobj oh; /* share holder */
-       char client_name[SM_MAXSTRLEN];
        short mode;
        short access;
+       char client_name[SM_MAXSTRLEN]; /* name is really variable length and must be last! */
 };
 LIST_HEAD(nfssharelist_head, file_share);
 
@@ -129,9 +122,10 @@ struct nfssharefilelist_head nfssharefilelist_head = LIST_HEAD_INITIALIZER(nfssh
 /* struct describing a monitored host */
 struct host {
        TAILQ_ENTRY(host) hostlst;
-       char name[SM_MAXSTRLEN];
        int refcnt;
        time_t lastuse;
+       struct sockaddr addr;
+       char name[SM_MAXSTRLEN]; /* name is really variable length and must be last! */
 };
 /* list of hosts we monitor */
 TAILQ_HEAD(hostlst_head, host);
@@ -174,7 +168,8 @@ enum hwlock_status { HW_GRANTED = 0, HW_GRANTED_DUPLICATE,
 
 enum partialfilelock_status { PFL_GRANTED=0, PFL_GRANTED_DUPLICATE, PFL_DENIED,
                              PFL_NFSDENIED, PFL_NFSBLOCKED, PFL_NFSDENIED_NOLOCK, PFL_NFSRESERR, 
-                             PFL_HWDENIED,  PFL_HWBLOCKED,  PFL_HWDENIED_NOLOCK, PFL_HWRESERR};
+                             PFL_HWDENIED,  PFL_HWBLOCKED,  PFL_HWDENIED_NOLOCK, PFL_HWRESERR,
+                             PFL_HWDENIED_STALEFH, PFL_HWDENIED_READONLY };
 
 enum LFLAGS {LEDGE_LEFT, LEDGE_LBOUNDARY, LEDGE_INSIDE, LEDGE_RBOUNDARY, LEDGE_RIGHT};
 enum RFLAGS {REDGE_LEFT, REDGE_LBOUNDARY, REDGE_INSIDE, REDGE_RBOUNDARY, REDGE_RIGHT};
@@ -187,17 +182,17 @@ int send_granted(struct file_lock *fl, int opcode);
 void siglock(void);
 void sigunlock(void);
 void destroy_lock_host(struct host *ihp);
-void monitor_lock_host(const char *hostname);
-void unmonitor_lock_host(const char *hostname);
+static void monitor_lock_host(const char *hostname, const struct sockaddr *addr);
 
 void   copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src,
     const bool_t exclusive, struct nlm4_holder *dest);
 struct file_lock *     allocate_file_lock(const netobj *lockowner,
-    const netobj *filehandle);
+    const netobj *filehandle, const struct sockaddr *addr,
+    const char *caller_name);
 void   deallocate_file_lock(struct file_lock *fl);
 void   fill_file_lock(struct file_lock *fl,
-    struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
-    const u_int64_t offset, const u_int64_t len, const char *caller_name,
+    const bool_t exclusive, const int32_t svid,
+    const u_int64_t offset, const u_int64_t len,
     const int state, const int status, const int flags, const int blocking);
 int    regions_overlap(const u_int64_t start1, const u_int64_t len1,
     const u_int64_t start2, const u_int64_t len2);;
@@ -238,10 +233,9 @@ enum partialfilelock_status        unlock_partialfilelock(
 void   clear_partialfilelock(const char *hostname);
 enum partialfilelock_status    test_partialfilelock(
     const struct file_lock *fl, struct file_lock **conflicting_fl);
-enum nlm_stats do_test(struct file_lock *fl,
-    struct file_lock **conflicting_fl);
-enum nlm_stats do_unlock(struct file_lock *fl);
-enum nlm_stats do_lock(struct file_lock *fl);
+enum nlm4_stats        do_test(struct file_lock *fl, struct file_lock **conflicting_fl);
+enum nlm4_stats        do_unlock(struct file_lock *fl);
+enum nlm4_stats        do_lock(struct file_lock *fl);
 void   do_clear(const char *hostname);
 
 
@@ -397,20 +391,40 @@ copy_nlm4_lock_to_nlm4_holder(src, exclusive, dest)
 }
 
 
+size_t
+strnlen(const char *s, size_t len)
+{
+    size_t n;
+
+    for (n = 0;  s[n] != 0 && n < len; n++)
+        ;
+    return n;
+}
+
 /*
  * allocate_file_lock: Create a lock with the given parameters
  */
 
 struct file_lock *
-allocate_file_lock(const netobj *lockowner, const netobj *filehandle)
+allocate_file_lock(const netobj *lockowner, const netobj *filehandle,
+                  const struct sockaddr *addr, const char *caller_name)
 {
        struct file_lock *newfl;
+       size_t n;
 
-       newfl = malloc(sizeof(struct file_lock));
+       /* Beware of rubbish input! */
+       n = strnlen(caller_name, SM_MAXSTRLEN);
+       if (n == SM_MAXSTRLEN) {
+               return NULL;
+       }
+
+       newfl = malloc(sizeof(*newfl) - sizeof(newfl->client_name) + n + 1);
        if (newfl == NULL) {
                return NULL;
        }
-       bzero(newfl, sizeof(newfl));
+       bzero(newfl, sizeof(*newfl) - sizeof(newfl->client_name));
+       memcpy(newfl->client_name, caller_name, n);
+       newfl->client_name[n] = 0;
 
        newfl->client.oh.n_bytes = malloc(lockowner->n_len);
        if (newfl->client.oh.n_bytes == NULL) {
@@ -429,6 +443,14 @@ allocate_file_lock(const netobj *lockowner, const netobj *filehandle)
        newfl->filehandle.n_len = filehandle->n_len;
        bcopy(filehandle->n_bytes, newfl->filehandle.n_bytes, filehandle->n_len);
 
+       newfl->addr = malloc(addr->sa_len);
+       if (newfl->addr == NULL) {
+               free(newfl->client.oh.n_bytes);
+               free(newfl);
+               return NULL;
+       }
+       memcpy(newfl->addr, addr, addr->sa_len);
+
        return newfl;
 }
 
@@ -437,19 +459,15 @@ allocate_file_lock(const netobj *lockowner, const netobj *filehandle)
  */
 void
 fill_file_lock(struct file_lock *fl,
-    struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
-    const u_int64_t offset, const u_int64_t len, const char *caller_name,
+    const bool_t exclusive, const int32_t svid,
+    const u_int64_t offset, const u_int64_t len,
     const int state, const int status, const int flags, const int blocking)
 {
-       fl->addr = addr;
-
        fl->client.exclusive = exclusive;
        fl->client.svid = svid;
        fl->client.l_offset = offset;
        fl->client.l_len = len;
 
-       strncpy(fl->client_name, caller_name, SM_MAXSTRLEN);
-
        fl->nsm_status = state;
        fl->status = status;
        fl->flags = flags;
@@ -462,6 +480,7 @@ fill_file_lock(struct file_lock *fl,
 void
 deallocate_file_lock(struct file_lock *fl)
 {
+       free(fl->addr);
        free(fl->client.oh.n_bytes);
        free(fl->filehandle.n_bytes);
        free(fl);
@@ -948,22 +967,21 @@ split_nfslock(exist_lock, unlock_lock, left_lock, right_lock)
            &start1, &len1, &start2, &len2);
 
        if ((spstatus & SPL_LOCK1) != 0) {
-               *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle);
+               *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name);
                if (*left_lock == NULL) {
                        debuglog("Unable to allocate resource for split 1\n");
                        return SPL_RESERR;
                }
 
                fill_file_lock(*left_lock,
-                   exist_lock->addr,
                    exist_lock->client.exclusive, exist_lock->client.svid,
                    start1, len1,
-                   exist_lock->client_name, exist_lock->nsm_status,
+                   exist_lock->nsm_status,
                    exist_lock->status, exist_lock->flags, exist_lock->blocking);
        }
 
        if ((spstatus & SPL_LOCK2) != 0) {
-               *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle);
+               *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name);
                if (*right_lock == NULL) {
                        debuglog("Unable to allocate resource for split 1\n");
                        if (*left_lock != NULL) {
@@ -973,10 +991,9 @@ split_nfslock(exist_lock, unlock_lock, left_lock, right_lock)
                }
 
                fill_file_lock(*right_lock,
-                   exist_lock->addr,
                    exist_lock->client.exclusive, exist_lock->client.svid,
                    start2, len2,
-                   exist_lock->client_name, exist_lock->nsm_status,
+                   exist_lock->nsm_status,
                    exist_lock->status, exist_lock->flags, exist_lock->blocking);
        }
 
@@ -1002,7 +1019,7 @@ unlock_nfslock(fl, released_lock, left_lock, right_lock)
 
        retval = NFS_DENIED_NOLOCK;
 
-       printf("Attempting to match lock...\n");
+       debuglog("Attempting to match lock...\n");
        mfl = get_lock_matching_unlock(fl);
 
        if (mfl != NULL) {
@@ -1063,6 +1080,7 @@ lock_hwlock(struct file_lock *fl)
 {
        struct monfile *imf,*nmf;
        int lflags, flerror;
+       fhandle_t fh;
 
        /* Scan to see if filehandle already present */
        LIST_FOREACH(imf, &monfilelist_head, monfilelist) {
@@ -1091,17 +1109,28 @@ lock_hwlock(struct file_lock *fl)
                return (HW_RESERR);
        }
        nmf->filehandle.n_bytes = malloc(fl->filehandle.n_len);
-       if (nmf == NULL) {
+       if (nmf->filehandle.n_bytes == NULL) {
                debuglog("hwlock resource allocation failure\n");
                free(nmf);
                return (HW_RESERR);
        }
 
+       if (fl->filehandle.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("hwlock: bad fh length %d (from %16s): %32s\n",
+                   fl->filehandle.n_len, fl->client_name, strerror(errno));
+               free(nmf->filehandle.n_bytes);
+               free(nmf);
+               return (HW_STALEFH);
+       }
+       fh.fh_len = fl->filehandle.n_len;
+       bcopy(fl->filehandle.n_bytes, fh.fh_data, fh.fh_len);
+
        /* XXX: Is O_RDWR always the correct mode? */
-       nmf->fd = fhopen((fhandle_t *)fl->filehandle.n_bytes, O_RDWR);
+       nmf->fd = fhopen(&fh, O_RDWR);
        if (nmf->fd < 0) {
                debuglog("fhopen failed (from %16s): %32s\n",
                    fl->client_name, strerror(errno));
+               free(nmf->filehandle.n_bytes);
                free(nmf);
                switch (errno) {
                case ESTALE:
@@ -1128,6 +1157,7 @@ lock_hwlock(struct file_lock *fl)
                debuglog("flock failed (from %16s): %32s\n",
                    fl->client_name, strerror(errno));
                close(nmf->fd);
+               free(nmf->filehandle.n_bytes);
                free(nmf);
                switch (errno) {
                case EAGAIN:
@@ -1185,6 +1215,7 @@ unlock_hwlock(const struct file_lock *fl)
        if (imf->refcount <= 0) {
                close(imf->fd);
                LIST_REMOVE(imf, monfilelist);
+               free(imf->filehandle.n_bytes);
                free(imf);
        }
        debuglog("Exiting unlock_hwlock (HW_GRANTED)\n");
@@ -1364,6 +1395,32 @@ retry_blockingfilelocklist(netobj *fh)
                                do_unlock(ifl);
                                /* ifl is NO LONGER VALID AT THIS POINT */
                        }
+               } else if (pflstatus == PFL_HWDENIED_STALEFH) {
+                       /*
+                        * Uh oh...
+                        * It would be nice if we could inform the client of
+                        * this error.  Unfortunately, there's no way to do
+                        * that in the NLM protocol (can't send "granted"
+                        * message with an error and there's no "never going
+                        * to be granted" message).
+                        *
+                        * Since there's no chance of this blocked request ever
+                        * succeeding, we drop the lock request rather than
+                        * needlessly keeping it around just to rot forever in
+                        * the blocked lock list.
+                        *
+                        * Hopefully, if the client is still waiting for the lock,
+                        * they will resend the request (and get an error then).
+                        *
+                        * XXX Note: PFL_HWDENIED_READONLY could potentially
+                        * be handled this way as well, although that would
+                        * only be an issue if a file system changed from
+                        * read-write to read-only out from under a blocked
+                        * lock request, and that's far less likely than a
+                        * file disappearing out from under such a request.
+                        */
+                       deallocate_file_lock(ifl);
+                       /* ifl is NO LONGER VALID AT THIS POINT */
                } else {
                        /* Reinsert lock back into same place in blocked list */
                        debuglog("Replacing blocked lock\n");
@@ -1374,9 +1431,10 @@ retry_blockingfilelocklist(netobj *fh)
                                LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist);
                }
 
-               if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) {
-                       /* If ifl was permanently removed from the list, (e.g the */
-                       /* lock was granted), pfl should remain where it's at. */
+               if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE ||
+                   pflstatus == PFL_HWDENIED_STALEFH) {
+                       /* If ifl was permanently removed from the list, (e.g it */
+                       /* was granted or dropped), pfl should remain where it's at. */
                } else {
                        /* If ifl was left in the list, (e.g it was reinserted back */
                        /* in place), pfl should simply be moved forward to be ifl */
@@ -1463,7 +1521,7 @@ lock_partialfilelock(struct file_lock *fl)
                                retval = PFL_GRANTED;
                        }
                        if (fl->flags & LOCK_MON)
-                               monitor_lock_host(fl->client_name);
+                               monitor_lock_host_by_name(fl->client_name);
                        break;
                case HW_RESERR:
                        debuglog("HW RESERR\n");
@@ -1473,6 +1531,18 @@ lock_partialfilelock(struct file_lock *fl)
                        debuglog("HW DENIED\n");
                        retval = PFL_HWDENIED;
                        break;
+               case HW_DENIED_NOLOCK:
+                       debuglog("HW DENIED NOLOCK\n");
+                       retval = PFL_HWDENIED_NOLOCK;
+                       break;
+               case HW_STALEFH:
+                       debuglog("HW STALE FH\n");
+                       retval = PFL_HWDENIED_STALEFH;
+                       break;
+               case HW_READONLY:
+                       debuglog("HW READONLY\n");
+                       retval = PFL_HWDENIED_READONLY;
+                       break;
                default:
                        debuglog("Unmatched hwstatus %d\n",hwstatus);
                        break;
@@ -1505,12 +1575,17 @@ lock_partialfilelock(struct file_lock *fl)
        if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) {
                /* Once last chance to check the lock */
                if (fl->blocking == 1) {
-                       /* Queue the lock */
-                       debuglog("BLOCKING LOCK RECEIVED\n");
-                       retval = (retval == PFL_NFSDENIED ?
-                           PFL_NFSBLOCKED : PFL_HWBLOCKED);
-                       add_blockingfilelock(fl);
-                       dump_filelock(fl);
+                       if (retval == PFL_NFSDENIED) {
+                               /* Queue the lock */
+                               debuglog("BLOCKING LOCK RECEIVED\n");
+                               retval = PFL_NFSBLOCKED;
+                               add_blockingfilelock(fl);
+                               dump_filelock(fl);
+                       } else {
+                               /* retval is okay as PFL_HWDENIED */
+                               debuglog("BLOCKING LOCK DENIED IN HARDWARE\n");
+                               dump_filelock(fl);
+                       }
                } else {
                        /* Leave retval alone, it's already correct */
                        debuglog("Lock denied.  Non-blocking failure\n");
@@ -1585,7 +1660,7 @@ unlock_partialfilelock(const struct file_lock *fl)
                                debuglog("HW duplicate lock failure for left split\n");
                        }
                        if (lfl->flags & LOCK_MON)
-                               monitor_lock_host(lfl->client_name);
+                               monitor_lock_host_by_name(lfl->client_name);
                }
 
                if (rfl != NULL) {
@@ -1595,7 +1670,7 @@ unlock_partialfilelock(const struct file_lock *fl)
                                debuglog("HW duplicate lock failure for right split\n");
                        }
                        if (rfl->flags & LOCK_MON)
-                               monitor_lock_host(rfl->client_name);
+                               monitor_lock_host_by_name(rfl->client_name);
                }
 
                switch (unlstatus) {
@@ -1699,6 +1774,7 @@ void
 clear_partialfilelock(const char *hostname)
 {
        struct file_lock *ifl, *nfl;
+       enum partialfilelock_status pfsret;
 
        /* Clear blocking file lock list */
        clear_blockingfilelock(hostname);
@@ -1712,7 +1788,7 @@ clear_partialfilelock(const char *hostname)
         * would mess up the iteration.  Thus, a next element
         * must be used explicitly
         */
-
+restart:
        ifl = LIST_FIRST(&nfslocklist_head);
 
        while (ifl != NULL) {
@@ -1720,8 +1796,21 @@ clear_partialfilelock(const char *hostname)
 
                if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) {
                        /* Unlock destroys ifl out from underneath */
-                       unlock_partialfilelock(ifl);
+                       pfsret = unlock_partialfilelock(ifl);
+                       if (pfsret != PFL_GRANTED) {
+                               /* Uh oh... there was some sort of problem. */
+                               /* If we restart the loop, we may get */
+                               /* stuck here forever getting errors. */
+                               /* So, let's just abort the whole scan. */
+                               syslog(LOG_WARNING, "lock clearing for %s failed: %d",
+                                       hostname, pfsret);
+                               break;
+                       }
                        /* ifl is NO LONGER VALID AT THIS POINT */
+                       /* Note: the unlock may deallocate several existing locks. */
+                       /* Therefore, we need to restart the scanning of the list, */
+                       /* because nfl could be pointing to a freed lock. */
+                       goto restart;
                }
                ifl = nfl;
        }
@@ -1775,11 +1864,11 @@ test_partialfilelock(const struct file_lock *fl,
  * the few return codes which the nlm subsystems wishes to trasmit
  */
 
-enum nlm_stats
+enum nlm4_stats
 do_test(struct file_lock *fl, struct file_lock **conflicting_fl)
 {
        enum partialfilelock_status pfsret;
-       enum nlm_stats retval;
+       enum nlm4_stats retval;
 
        debuglog("Entering do_test...\n");
 
@@ -1834,11 +1923,11 @@ do_test(struct file_lock *fl, struct file_lock **conflicting_fl)
  * convinced that this should be abstracted out and bounced up a level
  */
 
-enum nlm_stats
+enum nlm4_stats
 do_lock(struct file_lock *fl)
 {
        enum partialfilelock_status pfsret;
-       enum nlm_stats retval;
+       enum nlm4_stats retval;
 
        debuglog("Entering do_lock...\n");
 
@@ -1869,10 +1958,22 @@ do_lock(struct file_lock *fl)
                break;
        case PFL_NFSRESERR:
        case PFL_HWRESERR:
+       case PFL_NFSDENIED_NOLOCK:
+       case PFL_HWDENIED_NOLOCK:
                debuglog("PFL lock resource alocation fail\n");
                dump_filelock(fl);
                retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
                break;
+       case PFL_HWDENIED_STALEFH:
+               debuglog("PFL_NFS lock denied STALEFH");
+               dump_filelock(fl);
+               retval = (fl->flags & LOCK_V4) ? nlm4_stale_fh : nlm_denied;
+               break;
+       case PFL_HWDENIED_READONLY:
+               debuglog("PFL_NFS lock denied READONLY");
+               dump_filelock(fl);
+               retval = (fl->flags & LOCK_V4) ? nlm4_rofs : nlm_denied;
+               break;
        default:
                debuglog("PFL lock *FAILED*");
                dump_filelock(fl);
@@ -1885,11 +1986,11 @@ do_lock(struct file_lock *fl)
        return retval;
 }
 
-enum nlm_stats
+enum nlm4_stats
 do_unlock(struct file_lock *fl)
 {
        enum partialfilelock_status pfsret;
-       enum nlm_stats retval;
+       enum nlm4_stats retval;
 
        debuglog("Entering do_unlock...\n");
        pfsret = unlock_partialfilelock(fl);
@@ -1961,6 +2062,12 @@ testlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused)
 {
        struct file_lock test_fl, *conflicting_fl;
 
+       if (lock->fh.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("received fhandle size %d, max size %d",
+                   lock->fh.n_len, NFS_MAX_FH_SIZE);
+               return NULL;
+       }
+
        bzero(&test_fl, sizeof(test_fl));
 
        test_fl.filehandle.n_len = lock->fh.n_len;
@@ -1990,11 +2097,11 @@ testlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused)
  * will do the blocking lock.
  */
 
-enum nlm_stats
+enum nlm4_stats
 getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags)
 {
        struct file_lock *newfl;
-       enum nlm_stats retval;
+       enum nlm4_stats retval;
 
        debuglog("Entering getlock...\n");
 
@@ -2002,8 +2109,16 @@ getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags)
                return (flags & LOCK_V4) ?
                    nlm4_denied_grace_period : nlm_denied_grace_period;
 
+       if (lckarg->alock.fh.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("received fhandle size %d, max size %d",
+                   lckarg->alock.fh.n_len, NFS_MAX_FH_SIZE);
+               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+       }
+
        /* allocate new file_lock for this request */
-       newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->alock.fh);
+       newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->alock.fh,
+                                  (struct sockaddr *)svc_getcaller(rqstp->rq_xprt),
+                                  lckarg->alock.caller_name);
        if (newfl == NULL) {
                syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno));
                /* failed */
@@ -2011,16 +2126,10 @@ getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags)
                    nlm4_denied_nolocks : nlm_denied_nolocks;
        }
 
-       if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
-               debuglog("recieved fhandle size %d, local size %d",
-                   lckarg->alock.fh.n_len, (int)sizeof(fhandle_t));
-       }
-
        fill_file_lock(newfl,
-           (struct sockaddr *)svc_getcaller(rqstp->rq_xprt),
            lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset,
            lckarg->alock.l_len,
-           lckarg->alock.caller_name, lckarg->state, 0, flags, lckarg->block);
+           lckarg->state, 0, flags, lckarg->block);
        
        /*
         * newfl is now fully constructed and deallocate_file_lock
@@ -2057,15 +2166,21 @@ getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags)
 
 
 /* unlock a filehandle */
-enum nlm_stats
-unlock(nlm4_lock *lock, const int flags __unused)
+enum nlm4_stats
+unlock(nlm4_lock *lock, const int flags)
 {
        struct file_lock fl;
-       enum nlm_stats err;
-       
-       siglock();
+       enum nlm4_stats err;
        
        debuglog("Entering unlock...\n");
+
+       if (lock->fh.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("received fhandle size %d, max size %d",
+                   lock->fh.n_len, NFS_MAX_FH_SIZE);
+               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+       }
+       
+       siglock();
        
        bzero(&fl,sizeof(struct file_lock));
        fl.filehandle.n_len = lock->fh.n_len;
@@ -2083,17 +2198,23 @@ unlock(nlm4_lock *lock, const int flags __unused)
 }
 
 /* cancel a blocked lock request */
-enum nlm_stats
-cancellock(nlm4_cancargs *args, const int flags __unused)
+enum nlm4_stats
+cancellock(nlm4_cancargs *args, const int flags)
 {
        struct file_lock *ifl, *nfl;
-       enum nlm_stats err;
-
-       siglock();
+       enum nlm4_stats err;
 
        debuglog("Entering cancellock...\n");
 
-       err = nlm_denied;
+       if (args->alock.fh.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("received fhandle size %d, max size %d",
+                   args->alock.fh.n_len, NFS_MAX_FH_SIZE);
+               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+       }
+
+       siglock();
+
+       err = (flags & LOCK_V4) ? nlm4_denied : nlm_denied;
 
        /*
         * scan blocked lock list for matching request and remove/destroy
@@ -2137,7 +2258,7 @@ cancellock(nlm4_cancargs *args, const int flags __unused)
                /* got it */
                remove_blockingfilelock(ifl);
                deallocate_file_lock(ifl);
-               err = nlm_granted;
+               err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
                break;
        }
 
@@ -2152,7 +2273,7 @@ cancellock(nlm4_cancargs *args, const int flags __unused)
 /*
  * XXX: The following monitor/unmonitor routines 
  * have not been extensively tested (ie. no regression
- * script exists like for the locking sections
+ * script exists like for the locking sections)
  */
 
 /*
@@ -2163,24 +2284,38 @@ cancellock(nlm4_cancargs *args, const int flags __unused)
  *    enqueue it at the front of the "in use" queue.
  */
 struct host *
-get_lock_host(struct hostlst_head *hd, const char *hostname)
+get_lock_host(struct hostlst_head *hd, const char *hostname, const struct sockaddr *saddr)
 {
        struct host *ihp;
 
-       debuglog("get_lock_host %s\n", hostname);
+       if (!hostname && !saddr)
+               return (NULL);
+
+       debuglog("get_lock_host %s\n", hostname ? hostname : "addr");
        TAILQ_FOREACH(ihp, hd, hostlst) {
-               if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
-                       TAILQ_REMOVE(hd, ihp, hostlst);
-                       /* Host is already monitored, bump refcount */
+               if (hostname && (strncmp(hostname, ihp->name, SM_MAXSTRLEN) != 0))
+                       continue;
+               if (saddr && addrcmp(saddr, &ihp->addr))
+                       continue;
+               TAILQ_REMOVE(hd, ihp, hostlst);
+               /*
+                * Host is already monitored, so just bump the
+                * reference count.  But don't bump the reference
+                * count if we're adding additional client-side
+                * references.  Client-side monitors are done by
+                * address, are never unmonitored, and should only
+                * take one refcount.  Otherwise, repeated calls
+                * could cause the refcount to wrap.
+                */
+               if (!saddr || !ihp->addr.sa_len)
                        ++ihp->refcnt;
-                       ihp->lastuse = currsec;
-                       /* Host should only be in the monitor list once */
-                       TAILQ_INSERT_HEAD(&hostlst_head, ihp, hostlst);
-                       break;
-               }
+               ihp->lastuse = currsec;
+               /* Host should only be in the monitor list once */
+               TAILQ_INSERT_HEAD(&hostlst_head, ihp, hostlst);
+               break;
        }
        debuglog("get_lock_host %s %s\n",
-           ihp == NULL ? "did not find" : "found", hostname);
+           ihp == NULL ? "did not find" : "found", hostname ? hostname : "addr");
        return (ihp);
 }
 
@@ -2189,39 +2324,89 @@ get_lock_host(struct hostlst_head *hd, const char *hostname)
  * inform statd
  */
 void
-monitor_lock_host(const char *hostname)
+monitor_lock_host_by_name(const char *hostname)
 {
-       struct host *ihp, *nhp;
-       struct mon smon;
-       struct sm_stat_res sres;
-       int rpcret, statflag;
-
-       rpcret = 0;
-       statflag = 0;
+       struct host *ihp;
 
        debuglog("monitor_lock_host: %s\n", hostname);
-       ihp = get_lock_host(&hostlst_head, hostname);
+       ihp = get_lock_host(&hostlst_head, hostname, NULL);
        if (ihp == NULL)
-               ihp = get_lock_host(&hostlst_unref, hostname);
+               ihp = get_lock_host(&hostlst_unref, hostname, NULL);
        if (ihp != NULL) {
                debuglog("Monitor_lock_host: %s (cached)\n", hostname);
                return;
        }
 
-       debuglog("Monitor_lock_host: %s (not found, creating)\n", hostname);
-       /* Host is not yet monitored, add it */
-       nhp = malloc(sizeof(struct host));
+       monitor_lock_host(hostname, NULL);
+}
+
+void
+monitor_lock_host_by_addr(const struct sockaddr *saddr)
+{
+       struct host *ihp;
+       struct hostent *hp;
+       char hostaddr[SM_MAXSTRLEN];
+       struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+       if (getnameinfo(saddr, saddr->sa_len, hostaddr, sizeof(hostaddr),
+                       NULL, 0, NI_NUMERICHOST)) {
+               debuglog("monitor_lock_host: bad address\n");
+               return;
+       }
+       debuglog("monitor_lock_host: %s\n", hostaddr);
+       ihp = get_lock_host(&hostlst_head, NULL, saddr);
+       if (ihp == NULL)
+               ihp = get_lock_host(&hostlst_unref, NULL, saddr);
+       if (ihp != NULL) {
+               debuglog("Monitor_lock_host: %s (cached)\n", ihp->name);
+               return;
+       }
+
+       hp = gethostbyaddr((char*)&sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
+       if (hp) {
+               monitor_lock_host(hp->h_name, saddr);
+       } else {
+               // herror(hostaddr);
+               monitor_lock_host(hostaddr, saddr);
+       }
+}
+
+static void
+monitor_lock_host(const char *hostname, const struct sockaddr *saddr)
+{
+       struct host *nhp;
+       struct mon smon;
+       struct sm_stat_res sres;
+       int rpcret, statflag;
+       size_t n;
+
+       rpcret = 0;
+       statflag = 0;
 
+       /* Host is not yet monitored, add it */
+       debuglog("Monitor_lock_host: %s (creating)\n", hostname);
+       n = strnlen(hostname, SM_MAXSTRLEN);
+       if (n == SM_MAXSTRLEN) {
+               debuglog("monitor_lock_host: hostname too long\n");
+               return;
+       }
+       nhp = malloc(sizeof(*nhp) - sizeof(nhp->name) + n + 1);
        if (nhp == NULL) {
                debuglog("Unable to allocate entry for statd mon\n");
                return;
        }
 
        /* Allocated new host entry, now fill the fields */
-       strncpy(nhp->name, hostname, SM_MAXSTRLEN);
+       memcpy(nhp->name, hostname, n);
+       nhp->name[n] = 0;
        nhp->refcnt = 1;
        nhp->lastuse = currsec;
-       debuglog("Locally Monitoring host %16s\n",hostname);
+       if (saddr) {
+               bcopy(saddr, &nhp->addr, saddr->sa_len);
+       } else {
+               nhp->addr.sa_len = 0;
+       }
+       debuglog("Locally Monitoring host '%s'\n", hostname);
 
        debuglog("Attempting to tell statd\n");
 
@@ -2267,7 +2452,7 @@ unmonitor_lock_host(const char *hostname)
 
        TAILQ_FOREACH(ihp, &hostlst_head, hostlst) {
                if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
-                       /* Host is monitored, bump refcount */
+                       /* Host is unmonitored, drop refcount */
                        --ihp->refcnt;
                        /* Host should only be in the monitor list once */
                        break;
@@ -2509,11 +2694,12 @@ granted_failed(nlm4_res *arg)
 /*
  * getshare: try to acquire a share reservation
  */
-enum nlm_stats
+enum nlm4_stats
 getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
 {
        struct sharefile *shrfile;
        struct file_share *sh;
+       size_t n;
 
        debuglog("Entering getshare...\n");
 
@@ -2524,6 +2710,12 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
                        nlm_denied_grace_period;
        }
 
+       if (shrarg->share.fh.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("received fhandle size %d, max size %d",
+                   shrarg->share.fh.n_len, NFS_MAX_FH_SIZE);
+               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+       }
+
        /* find file in list of share files */
        LIST_FOREACH(shrfile, &nfssharefilelist_head, sharefilelist) {
                if ((shrarg->share.fh.n_len == shrfile->filehandle.n_len) &&
@@ -2536,8 +2728,11 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
 
        /* if share file not found, create a new share file */
        if (!shrfile) {
+               fhandle_t fh;
                int fd;
-               fd = fhopen((fhandle_t *)shrarg->share.fh.n_bytes, O_RDONLY);
+               fh.fh_len = shrarg->share.fh.n_len;
+               bcopy(shrarg->share.fh.n_bytes, fh.fh_data, fh.fh_len);
+               fd = fhopen(&fh, O_RDONLY);
                if (fd < 0) {
                        debuglog("fhopen failed (from %16s): %32s\n",
                            shrarg->share.caller_name, strerror(errno));
@@ -2554,7 +2749,7 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
                if (!shrfile) {
                        debuglog("getshare failed: can't allocate sharefile\n");
                        close(fd);
-                       return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+                       return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
                }
                shrfile->filehandle.n_len = shrarg->share.fh.n_len;
                shrfile->filehandle.n_bytes = malloc(shrarg->share.fh.n_len);
@@ -2562,7 +2757,7 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
                        debuglog("getshare failed: can't allocate sharefile filehandle\n");
                        free(shrfile);
                        close(fd);
-                       return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+                       return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
                }
                bcopy(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes,
                        shrarg->share.fh.n_len);
@@ -2581,18 +2776,24 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
                        sh->mode = shrarg->share.mode;
                        sh->access = shrarg->share.access;
                        debuglog("getshare: updated existing share\n");
-                       return nlm_granted;
+                       return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
                }
                if (((shrarg->share.mode & sh->access) != 0) ||
                    ((shrarg->share.access & sh->mode) != 0)) {
                        /* share request conflicts with existing share */
                        debuglog("getshare: conflicts with existing share\n");
-                       return nlm_denied;
+                       return (flags & LOCK_V4) ? nlm4_denied : nlm_denied;
                }
        }
 
        /* create/init new share */
-       sh = malloc(sizeof(struct file_share));
+       n = strnlen(shrarg->share.caller_name, SM_MAXSTRLEN);
+       if (n < SM_MAXSTRLEN) {
+               sh = malloc(sizeof(*sh) - sizeof(sh->client_name) + n + 1);
+       } else {
+               debuglog("getshare failed: hostname too long\n");
+               sh = NULL;
+       }
        if (!sh) {
                debuglog("getshare failed: can't allocate share\n");
                if (!shrfile->refcount) {
@@ -2601,8 +2802,9 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
                        free(shrfile->filehandle.n_bytes);
                        free(shrfile);
                }
-               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+               return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
        }
+       bzero(sh, sizeof(*sh) - sizeof(sh->client_name));
        sh->oh.n_len = shrarg->share.oh.n_len;
        sh->oh.n_bytes = malloc(sh->oh.n_len);
        if (!sh->oh.n_bytes) {
@@ -2614,9 +2816,10 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
                        free(shrfile->filehandle.n_bytes);
                        free(shrfile);
                }
-               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+               return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
        }
-       strncpy(sh->client_name, shrarg->share.caller_name, SM_MAXSTRLEN);
+       memcpy(sh->client_name, shrarg->share.caller_name, n);
+       sh->client_name[n] = 0;
        sh->mode = shrarg->share.mode;
        sh->access = shrarg->share.access;
 
@@ -2626,19 +2829,25 @@ getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
 
        debuglog("Exiting getshare...\n");
 
-       return nlm_granted;
+       return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
 }
 
 
 /* remove a share reservation */
-enum nlm_stats
-unshare(nlm_shareargs *shrarg, struct svc_req *rqstp)
+enum nlm4_stats
+unshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags)
 {
        struct sharefile *shrfile;
        struct file_share *sh;
 
        debuglog("Entering unshare...\n");
 
+       if (shrarg->share.fh.n_len > NFS_MAX_FH_SIZE) {
+               debuglog("received fhandle size %d, max size %d",
+                   shrarg->share.fh.n_len, NFS_MAX_FH_SIZE);
+               return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+       }
+
        /* find file in list of share files */
        LIST_FOREACH(shrfile, &nfssharefilelist_head, sharefilelist) {
                if ((shrarg->share.fh.n_len == shrfile->filehandle.n_len) &&
@@ -2652,7 +2861,7 @@ unshare(nlm_shareargs *shrarg, struct svc_req *rqstp)
        /* if share file not found, return success (per spec) */
        if (!shrfile) {
                debuglog("unshare: no such share file\n");
-               return nlm_granted;
+               return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
        }
 
        /* find share */
@@ -2666,7 +2875,7 @@ unshare(nlm_shareargs *shrarg, struct svc_req *rqstp)
        /* if share not found, return success (per spec) */
        if (!sh) {
                debuglog("unshare: no such share\n");
-               return nlm_granted;
+               return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
        }
 
        /* remove share from file and deallocate */
@@ -2686,7 +2895,7 @@ unshare(nlm_shareargs *shrarg, struct svc_req *rqstp)
 
        debuglog("Exiting unshare...\n");
 
-       return nlm_granted;
+       return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
 }
 
 /*
@@ -2701,6 +2910,7 @@ do_free_all(const char *hostname)
        struct file_lock *ifl, *nfl;
        struct sharefile *shrfile, *nshrfile;
        struct file_share *ifs, *nfs;
+       enum partialfilelock_status pfsret;
 
        /* clear non-monitored blocking file locks */
        ifl = LIST_FIRST(&blockedlocklist_head);
@@ -2717,6 +2927,7 @@ do_free_all(const char *hostname)
        }
 
        /* clear non-monitored file locks */
+restart:
        ifl = LIST_FIRST(&nfslocklist_head);
        while (ifl != NULL) {
                nfl = LIST_NEXT(ifl, nfslocklist);
@@ -2724,8 +2935,21 @@ do_free_all(const char *hostname)
                if (((ifl->flags & LOCK_MON) == 0) &&
                    (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0)) {
                        /* Unlock destroys ifl out from underneath */
-                       unlock_partialfilelock(ifl);
+                       pfsret = unlock_partialfilelock(ifl);
+                       if (pfsret != PFL_GRANTED) {
+                               /* Uh oh... there was some sort of problem. */
+                               /* If we restart the loop, we may get */
+                               /* stuck here forever getting errors. */
+                               /* So, let's just abort the whole scan. */
+                               syslog(LOG_WARNING, "unmonitored lock clearing for %s failed: %d",
+                                       hostname, pfsret);
+                               break;
+                       }
                        /* ifl is NO LONGER VALID AT THIS POINT */
+                       /* Note: the unlock may deallocate several existing locks. */
+                       /* Therefore, we need to restart the scanning of the list, */
+                       /* because nfl could be pointing to a freed lock. */
+                       goto restart;
                }
 
                ifl = nfl;