#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
*
netobj filehandle; /* NFS filehandle */
struct sockaddr *addr;
struct nlm4_holder client; /* lock holder */
- /* XXX: client_cookie used *only* in send_granted */
- netobj client_cookie; /* cookie sent by the client */
- char client_name[SM_MAXSTRLEN];
+ u_int64_t granted_cookie;
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);
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);
/* struct describing a monitored host */
struct host {
- LIST_ENTRY(host) hostlst;
- char name[SM_MAXSTRLEN];
+ TAILQ_ENTRY(host) hostlst;
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 */
-LIST_HEAD(hostlst_head, host);
-struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
+TAILQ_HEAD(hostlst_head, host);
+struct hostlst_head hostlst_head = TAILQ_HEAD_INITIALIZER(hostlst_head);
+struct hostlst_head hostlst_unref = TAILQ_HEAD_INITIALIZER(hostlst_unref);
+
+int host_expire = 60; /* seconds */
+time_t currsec;
+u_int64_t send_granted_cookie = 0;
/*
* File monitoring handlers
enum partialfilelock_status lock_partialfilelock(struct file_lock *fl);
-void send_granted(struct file_lock *fl, int opcode);
+int send_granted(struct file_lock *fl, int opcode);
void siglock(void);
void sigunlock(void);
-void monitor_lock_host(const char *hostname);
-void unmonitor_lock_host(const char *hostname);
+void destroy_lock_host(struct host *ihp);
+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 *matchcookie, 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);;
struct file_lock **conflicting_fl);
void remove_blockingfilelock(struct file_lock *fl);
void clear_blockingfilelock(const char *hostname);
-void retry_blockingfilelocklist(void);
+void retry_blockingfilelocklist(netobj *fh);
enum partialfilelock_status unlock_partialfilelock(
const struct file_lock *fl);
void clear_partialfilelock(const char *hostname);
debuglog("Dumping client identity:\n");
dump_netobj(&fl->client.oh);
- debuglog("Dumping client cookie:\n");
- dump_netobj(&fl->client_cookie);
-
debuglog("nsm: %d status: %d flags: %d locker: %d"
" fd: %d\n", fl->nsm_status, fl->status,
fl->flags, fl->locker, fl->fd);
}
+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 *matchcookie, 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;
+
+ /* Beware of rubbish input! */
+ n = strnlen(caller_name, SM_MAXSTRLEN);
+ if (n == SM_MAXSTRLEN) {
+ return NULL;
+ }
- newfl = malloc(sizeof(struct file_lock));
+ 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) {
newfl->client.oh.n_len = lockowner->n_len;
bcopy(lockowner->n_bytes, newfl->client.oh.n_bytes, lockowner->n_len);
- newfl->client_cookie.n_bytes = malloc(matchcookie->n_len);
- if (newfl->client_cookie.n_bytes == NULL) {
+ newfl->filehandle.n_bytes = malloc(filehandle->n_len);
+ if (newfl->filehandle.n_bytes == NULL) {
free(newfl->client.oh.n_bytes);
free(newfl);
return NULL;
}
- newfl->client_cookie.n_len = matchcookie->n_len;
- bcopy(matchcookie->n_bytes, newfl->client_cookie.n_bytes, matchcookie->n_len);
+ newfl->filehandle.n_len = filehandle->n_len;
+ bcopy(filehandle->n_bytes, newfl->filehandle.n_bytes, filehandle->n_len);
- newfl->filehandle.n_bytes = malloc(filehandle->n_len);
- if (newfl->filehandle.n_bytes == NULL) {
- free(newfl->client_cookie.n_bytes);
+ newfl->addr = malloc(addr->sa_len);
+ if (newfl->addr == NULL) {
free(newfl->client.oh.n_bytes);
free(newfl);
return NULL;
}
- newfl->filehandle.n_len = filehandle->n_len;
- bcopy(filehandle->n_bytes, newfl->filehandle.n_bytes, filehandle->n_len);
+ memcpy(newfl->addr, addr, addr->sa_len);
return newfl;
}
*/
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;
void
deallocate_file_lock(struct file_lock *fl)
{
+ free(fl->addr);
free(fl->client.oh.n_bytes);
- free(fl->client_cookie.n_bytes);
free(fl->filehandle.n_bytes);
free(fl);
}
/*
* get_lock_matching_unlock: Return a lock which matches the given unlock lock
- * or NULL otehrwise
+ * or NULL otherwise
* XXX: It is a shame that this duplicates so much code from test_nfslock.
*/
struct file_lock *
&start1, &len1, &start2, &len2);
if ((spstatus & SPL_LOCK1) != 0) {
- *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie,
- &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->client_cookie,
- &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) {
}
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);
}
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) {
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 (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:
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:
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");
void
add_blockingfilelock(struct file_lock *fl)
{
+ struct file_lock *ifl, *nfl;
debuglog("Entering add_blockingfilelock\n");
+ /*
+ * Check for a duplicate lock request.
+ * If found, deallocate the older request.
+ */
+ ifl = LIST_FIRST(&blockedlocklist_head);
+ for (; ifl != NULL; ifl = nfl) {
+ debuglog("Pointer to file lock: %p\n",ifl);
+ debuglog("****Dump of ifl****\n");
+ dump_filelock(ifl);
+ debuglog("*******************\n");
+
+ nfl = LIST_NEXT(ifl, nfslocklist);
+
+ if (fl->filehandle.n_len != ifl->filehandle.n_len)
+ continue;
+ if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes,
+ fl->filehandle.n_len))
+ continue;
+
+ /* Filehandles match, check region */
+ if ((fl->client.l_offset != ifl->client.l_offset) ||
+ (fl->client.l_len != ifl->client.l_len))
+ continue;
+
+ /* Regions match, check the identity */
+ if (!same_filelock_identity(fl,ifl))
+ continue;
+
+ debuglog("add_blockingfilelock: removing duplicate lock request.\n");
+ remove_blockingfilelock(ifl);
+ deallocate_file_lock(ifl);
+ break;
+ }
+
/*
* Clear the blocking flag so that it can be reused without
* adding it to the blocking queue a second time
}
}
+int need_retry_blocked_locks = 0; /* need to call retry_blockingfilelocklist() */
+
void
-retry_blockingfilelocklist(void)
+retry_blockingfilelocklist(netobj *fh)
{
- /* Retry all locks in the blocked list */
+ /*
+ * If fh is given, then retry just the locks with the
+ * same filehandle in the blocked list.
+ * Otherwise, simply retry all locks in the blocked list.
+ */
struct file_lock *ifl, *nfl, *pfl; /* Iterator */
enum partialfilelock_status pflstatus;
+ int rv;
debuglog("Entering retry_blockingfilelocklist\n");
+ need_retry_blocked_locks = 0;
+
pfl = NULL;
ifl = LIST_FIRST(&blockedlocklist_head);
debuglog("Iterator choice %p\n",ifl);
debuglog("Prev iterator choice %p\n",pfl);
debuglog("Next iterator choice %p\n",nfl);
+ /* if given a filehandle, only retry locks for the same filehandle */
+ if (fh && !same_netobj(fh, &ifl->filehandle)) {
+ ifl = nfl;
+ continue;
+ }
+
/*
* SUBTLE BUG: The file_lock must be removed from the
* old list so that it's list pointers get disconnected
if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) {
debuglog("Granted blocked lock\n");
/* lock granted and is now being used */
- send_granted(ifl,0);
- /* XXX should undo lock if send_granted fails */
+ rv = send_granted(ifl, 0);
+ if (rv) {
+ /*
+ * Uh oh... the NLM_GRANTED message failed.
+ * About the only thing we can do is drop the lock.
+ * Note: this could be bad if the error was only
+ * transient. Hopefully, if the client is still
+ * waiting for the lock, they will resend the request.
+ */
+ do_unlock(ifl);
+ /* ifl is NO LONGER VALID AT THIS POINT */
+ }
} else {
/* Reinsert lock back into same place in blocked list */
debuglog("Replacing blocked lock\n");
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. */
+ } 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 */
+ pfl = ifl;
+ }
/* Valid increment behavior regardless of state of ifl */
ifl = nfl;
- /* if a lock was granted incrementing pfl would make it nfl */
- if (pfl != NULL && (LIST_NEXT(pfl, nfslocklist) != nfl))
- pfl = LIST_NEXT(pfl, nfslocklist);
- else
- pfl = LIST_FIRST(&blockedlocklist_head);
}
debuglog("Exiting retry_blockingfilelocklist\n");
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");
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");
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) {
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) {
// XXX Workaround is to move this to nlm_prot_svc.c
// XXX after the unlock response is sent.
// retry_blockingfilelocklist();
+ need_retry_blocked_locks = 1;
break;
case NFS_DENIED_NOLOCK:
retval = PFL_GRANTED;
{
struct file_lock test_fl, *conflicting_fl;
+ if (lock->fh.n_len != sizeof(fhandle_t)) {
+ debuglog("received fhandle size %d, local size %d",
+ lock->fh.n_len, (int)sizeof(fhandle_t));
+ return NULL;
+ }
+
bzero(&test_fl, sizeof(test_fl));
test_fl.filehandle.n_len = lock->fh.n_len;
return (flags & LOCK_V4) ?
nlm4_denied_grace_period : nlm_denied_grace_period;
+ if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
+ debuglog("received fhandle size %d, local size %d",
+ lckarg->alock.fh.n_len, (int)sizeof(fhandle_t));
+ return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ }
+
/* allocate new file_lock for this request */
- newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->cookie, &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 */
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
/* unlock a filehandle */
enum nlm_stats
-unlock(nlm4_lock *lock, const int flags __unused)
+unlock(nlm4_lock *lock, const int flags)
{
struct file_lock fl;
enum nlm_stats err;
- siglock();
-
debuglog("Entering unlock...\n");
+
+ if (lock->fh.n_len != sizeof(fhandle_t)) {
+ debuglog("received fhandle size %d, local size %d",
+ lock->fh.n_len, (int)sizeof(fhandle_t));
+ return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ }
+
+ siglock();
bzero(&fl,sizeof(struct file_lock));
fl.filehandle.n_len = lock->fh.n_len;
return err;
}
+/* cancel a blocked lock request */
+enum nlm_stats
+cancellock(nlm4_cancargs *args, const int flags)
+{
+ struct file_lock *ifl, *nfl;
+ enum nlm_stats err;
+
+ debuglog("Entering cancellock...\n");
+
+ if (args->alock.fh.n_len != sizeof(fhandle_t)) {
+ debuglog("received fhandle size %d, local size %d",
+ args->alock.fh.n_len, (int)sizeof(fhandle_t));
+ return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ }
+
+ siglock();
+
+ err = nlm_denied;
+
+ /*
+ * scan blocked lock list for matching request and remove/destroy
+ */
+ ifl = LIST_FIRST(&blockedlocklist_head);
+ for ( ; ifl != NULL; ifl = nfl) {
+ nfl = LIST_NEXT(ifl, nfslocklist);
+
+ /* compare lock fh - filehandle */
+ if (!same_netobj(&args->alock.fh, &ifl->filehandle))
+ continue;
+
+ /* compare lock caller_name - client_name */
+ if (strncmp(args->alock.caller_name, ifl->client_name, SM_MAXSTRLEN))
+ continue;
+
+ /* Note: done't compare cookie - client_cookie */
+ /* The cookie may be specific to the cancel request */
+ /* and not be the same as the one in the original lock request. */
+
+ /* compare lock oh - client.oh */
+ if (!same_netobj(&args->alock.oh, &ifl->client.oh))
+ continue;
+
+ /* compare lock svid - client.svid */
+ if (args->alock.svid != ifl->client.svid)
+ continue;
+
+ /* compare lock l_offset - client.l_offset */
+ if (args->alock.l_offset != ifl->client.l_offset)
+ continue;
+
+ /* compare lock l_len - client.l_len */
+ if (args->alock.l_len != ifl->client.l_len)
+ continue;
+
+ /* compare exclusive - client.exclusive */
+ if (args->exclusive != ifl->client.exclusive)
+ continue;
+
+ /* got it */
+ remove_blockingfilelock(ifl);
+ deallocate_file_lock(ifl);
+ err = nlm_granted;
+ break;
+ }
+
+ sigunlock();
+
+ debuglog("Exiting cancellock...\n");
+
+ return err;
+}
+
+
/*
* 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)
+ */
+
+/*
+ * Find a lock host on a queue. If found:
+ * bump the ref,
+ * bump the access time,
+ * dequeue it from the queue it was found on,
+ * enqueue it at the front of the "in use" queue.
*/
+struct host *
+get_lock_host(struct hostlst_head *hd, const char *hostname, const struct sockaddr *saddr)
+{
+ struct host *ihp;
+
+ if (!hostname && !saddr)
+ return (NULL);
+
+ debuglog("get_lock_host %s\n", hostname ? hostname : "addr");
+ TAILQ_FOREACH(ihp, hd, hostlst) {
+ 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;
+ }
+ debuglog("get_lock_host %s %s\n",
+ ihp == NULL ? "did not find" : "found", hostname ? hostname : "addr");
+ return (ihp);
+}
/*
* monitor_lock_host: monitor lock hosts locally with a ref count and
* inform statd
*/
void
-monitor_lock_host(const char *hostname)
+monitor_lock_host_by_name(const char *hostname)
+{
+ struct host *ihp;
+
+ debuglog("monitor_lock_host: %s\n", hostname);
+ ihp = get_lock_host(&hostlst_head, hostname, NULL);
+ if (ihp == NULL)
+ ihp = get_lock_host(&hostlst_unref, hostname, NULL);
+ if (ihp != NULL) {
+ debuglog("Monitor_lock_host: %s (cached)\n", hostname);
+ return;
+ }
+
+ 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 *ihp, *nhp;
+ struct host *nhp;
struct mon smon;
struct sm_stat_res sres;
int rpcret, statflag;
-
+ size_t n;
+
rpcret = 0;
statflag = 0;
- LIST_FOREACH(ihp, &hostlst_head, hostlst) {
- if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
- /* Host is already monitored, bump refcount */
- ++ihp->refcnt;
- /* Host should only be in the monitor list once */
- return;
- }
- }
-
/* Host is not yet monitored, add it */
- nhp = malloc(sizeof(struct host));
-
+ 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;
- debuglog("Locally Monitoring host %16s\n",hostname);
-
+ nhp->lastuse = currsec;
+ 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");
-
+
bzero(&smon,sizeof(smon));
-
+
smon.mon_id.mon_name = nhp->name;
smon.mon_id.my_id.my_name = "localhost\0";
smon.mon_id.my_id.my_prog = NLM_PROG;
smon.mon_id.my_id.my_vers = NLM_SM;
smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
-
+
rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon,
&smon, xdr_sm_stat_res, &sres);
-
+
if (rpcret == 0) {
if (sres.res_stat == stat_fail) {
debuglog("Statd call failed\n");
rpcret);
statflag = 0;
}
-
+
if (statflag == 1) {
- LIST_INSERT_HEAD(&hostlst_head, nhp, hostlst);
+ TAILQ_INSERT_HEAD(&hostlst_head, nhp, hostlst);
} else {
free(nhp);
}
-
}
/*
unmonitor_lock_host(const char *hostname)
{
struct host *ihp;
- struct mon_id smon_id;
- struct sm_stat smstat;
- int rpcret;
-
- rpcret = 0;
- for( ihp=LIST_FIRST(&hostlst_head); ihp != NULL;
- ihp=LIST_NEXT(ihp, hostlst)) {
+ 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;
return;
if (ihp->refcnt < 0) {
- debuglog("Negative refcount!: %d\n",
- ihp->refcnt);
+ debuglog("Negative refcount!: %d\n", ihp->refcnt);
}
- debuglog("Attempting to unmonitor host %16s\n", hostname);
+ TAILQ_REMOVE(&hostlst_head, ihp, hostlst);
+ TAILQ_INSERT_HEAD(&hostlst_unref, ihp, hostlst);
+ if (host_expire <= 0)
+ destroy_lock_host(ihp);
+}
+
+void
+destroy_lock_host(struct host *ihp)
+{
+ struct mon_id smon_id;
+ struct sm_stat smstat;
+ int rpcret;
+
+ debuglog("Attempting to unmonitor host %16s\n", ihp->name);
bzero(&smon_id,sizeof(smon_id));
- smon_id.mon_name = (char *)hostname;
+ smon_id.mon_name = (char *)ihp->name;
smon_id.my_id.my_name = "localhost";
smon_id.my_id.my_prog = NLM_PROG;
smon_id.my_id.my_vers = NLM_SM;
smon_id.my_id.my_proc = NLM_SM_NOTIFY;
- rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON, xdr_mon,
- &smon_id, xdr_sm_stat_res, &smstat);
+ rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON, xdr_mon_id,
+ &smon_id, xdr_sm_stat, &smstat);
if (rpcret != 0) {
debuglog("Rpc call to unmonitor statd failed with "
- " return value: %d\n", rpcret);
+ " return value: %d: %s", rpcret, clnt_sperrno(rpcret));
+ } else {
+ debuglog("Succeeded unmonitoring %16s\n", ihp->name);
}
- LIST_REMOVE(ihp, hostlst);
+ TAILQ_REMOVE(&hostlst_unref, ihp, hostlst);
free(ihp);
}
+/*
+ * returns 1 if there are hosts to expire or 0 if there are none.
+ */
+int
+expire_lock_hosts(void)
+{
+ struct host *ihp;
+
+ debuglog("expire_lock_hosts: called\n");
+ for ( ;; ) {
+ ihp = TAILQ_LAST(&hostlst_unref, hostlst_head);
+ if (ihp == NULL)
+ break;
+ if (host_expire > 0 && ihp->lastuse >= currsec - host_expire)
+ break;
+ debuglog("expire_lock_hosts: expiring %s %d %d %d\n",
+ ihp->name, (int)ihp->lastuse,
+ (int)currsec, (int)currsec - host_expire);
+ destroy_lock_host(ihp);
+ }
+ return (TAILQ_LAST(&hostlst_unref, hostlst_head) != NULL);
+}
+
/*
* notify: Clear all locks from a host if statd complains
*
debuglog("Leaving notify\n");
}
-void
+int
send_granted(fl, opcode)
struct file_lock *fl;
int opcode __unused;
CLIENT *cli;
static char dummy;
struct timeval timeo;
- int success;
+ enum clnt_stat rv;
static struct nlm_res retval;
static struct nlm4_res retval4;
debuglog("About to send granted on blocked lock\n");
- //sleep(1);
- debuglog("Blowing off return send\n");
cli = get_client(fl->addr,
(fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
* The client will timeout and retry, the lock will be
* granted at this time.
*/
- return;
+ return -1;
}
timeo.tv_sec = 0;
timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
+ fl->granted_cookie = ++send_granted_cookie;
+ if (!send_granted_cookie)
+ send_granted_cookie++;
+
if (fl->flags & LOCK_V4) {
static nlm4_testargs res;
- res.cookie = fl->client_cookie;
+ res.cookie.n_len = sizeof(fl->granted_cookie);
+ res.cookie.n_bytes = (char*)&fl->granted_cookie;
res.exclusive = fl->client.exclusive;
res.alock.caller_name = fl->client_name;
res.alock.fh.n_len = fl->filehandle.n_len;
debuglog("sending v4 reply%s",
(fl->flags & LOCK_ASYNC) ? " (async)":"");
if (fl->flags & LOCK_ASYNC) {
- success = clnt_call(cli, NLM4_GRANTED_MSG,
+ rv = clnt_call(cli, NLM4_GRANTED_MSG,
xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo);
} else {
- success = clnt_call(cli, NLM4_GRANTED,
+ rv = clnt_call(cli, NLM4_GRANTED,
xdr_nlm4_testargs, &res, xdr_nlm4_res,
&retval4, timeo);
}
} else {
static nlm_testargs res;
- res.cookie = fl->client_cookie;
+ res.cookie.n_len = sizeof(fl->granted_cookie);
+ res.cookie.n_bytes = (char*)&fl->granted_cookie;
res.exclusive = fl->client.exclusive;
res.alock.caller_name = fl->client_name;
res.alock.fh.n_len = fl->filehandle.n_len;
debuglog("sending v1 reply%s",
(fl->flags & LOCK_ASYNC) ? " (async)":"");
if (fl->flags & LOCK_ASYNC) {
- success = clnt_call(cli, NLM_GRANTED_MSG,
+ rv = clnt_call(cli, NLM_GRANTED_MSG,
xdr_nlm_testargs, &res, xdr_void, &dummy, timeo);
} else {
- success = clnt_call(cli, NLM_GRANTED,
+ rv = clnt_call(cli, NLM_GRANTED,
xdr_nlm_testargs, &res, xdr_nlm_res,
&retval, timeo);
}
}
if (debug_level > 2)
debuglog("clnt_call returns %d(%s) for granted",
- success, clnt_sperrno(success));
+ rv, clnt_sperrno(rv));
+
+ if ((rv != RPC_SUCCESS) &&
+ !((fl->flags & LOCK_ASYNC) && (rv == RPC_TIMEDOUT)))
+ return -1;
+ return 0;
+}
+/*
+ * granted_failed: remove a granted lock that wasn't successfully
+ * accepted by the client
+ */
+void
+granted_failed(nlm4_res *arg)
+{
+ u_int64_t cookie;
+ struct file_lock *ifl;
+
+ debuglog("Entering granted_failed, status %d\n", arg->stat.stat);
+
+ if (arg->cookie.n_len != sizeof(cookie)) {
+ debuglog("Exiting granted_failed: bogus cookie size %d\n",
+ arg->cookie.n_len);
+ return;
+ }
+ bcopy(arg->cookie.n_bytes, &cookie, sizeof(cookie));
+ debuglog("granted_failed, cookie 0x%llx\n", cookie);
+
+ LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) {
+ debuglog("Pointer to file lock: %p\n",ifl);
+
+ debuglog("****Dump of ifl****\n");
+ dump_filelock(ifl);
+
+ if (ifl->granted_cookie != cookie)
+ continue;
+
+ debuglog("granted_failed: cookie found\n");
+ break;
+ }
+
+ if (ifl) {
+ do_unlock(ifl);
+ /* ifl is NO LONGER VALID AT THIS POINT */
+ } else {
+ debuglog("granted_failed: cookie NOT FOUND\n");
+ }
+
+ debuglog("Exiting granted_failed\n");
}
/*
{
struct sharefile *shrfile;
struct file_share *sh;
+ size_t n;
debuglog("Entering getshare...\n");
nlm_denied_grace_period;
}
+ if (shrarg->share.fh.n_len != sizeof(fhandle_t)) {
+ debuglog("received fhandle size %d, local size %d",
+ shrarg->share.fh.n_len, (int)sizeof(fhandle_t));
+ 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) &&
}
/* 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) {
}
return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
}
+ 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) {
}
return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
}
- 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;
/* remove a share reservation */
enum nlm_stats
-unshare(nlm_shareargs *shrarg, struct svc_req *rqstp)
+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 != sizeof(fhandle_t)) {
+ debuglog("received fhandle size %d, local size %d",
+ shrarg->share.fh.n_len, (int)sizeof(fhandle_t));
+ 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) &&