Implement partial-file NFS lock testing.
Submitted by: "Andrew P. Lentvorski" <andrewl@io.com>
This commit is contained in:
parent
a387081c63
commit
bad584e319
@ -87,6 +87,40 @@ log_from_addr(fun_name, req)
|
||||
syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
|
||||
}
|
||||
|
||||
/* log_netobj ----------------------------------------------------------- */
|
||||
/*
|
||||
* Purpose: Log a netobj
|
||||
* Returns: Nothing
|
||||
* Notes: This function should only really be called as part of
|
||||
* a debug subsystem.
|
||||
*/
|
||||
static void
|
||||
log_netobj(obj)
|
||||
netobj *obj;
|
||||
{
|
||||
char objvalbuffer[(sizeof(char)*2)*MAX_NETOBJ_SZ+2];
|
||||
char objascbuffer[sizeof(char)*MAX_NETOBJ_SZ+1];
|
||||
int i, maxlen;
|
||||
char *tmp1, *tmp2;
|
||||
|
||||
/* Notify of potential security attacks */
|
||||
if (obj->n_len > MAX_NETOBJ_SZ) {
|
||||
syslog(LOG_DEBUG, "SOMEONE IS TRYING TO DO SOMETHING NASTY!\n");
|
||||
syslog(LOG_DEBUG, "netobj too large! Should be %d was %d\n",
|
||||
MAX_NETOBJ_SZ, obj->n_len);
|
||||
}
|
||||
/* Prevent the security hazard from the buffer overflow */
|
||||
maxlen = (obj->n_len < MAX_NETOBJ_SZ ? obj->n_len : MAX_NETOBJ_SZ);
|
||||
for (i=0, tmp1 = objvalbuffer, tmp2 = objascbuffer; i < obj->n_len;
|
||||
i++, tmp1 +=2, tmp2 +=1) {
|
||||
sprintf(tmp1,"%02X",*(obj->n_bytes+i));
|
||||
sprintf(tmp2,"%c",*(obj->n_bytes+i));
|
||||
}
|
||||
*tmp1 = '\0';
|
||||
*tmp2 = '\0';
|
||||
syslog(LOG_DEBUG,"netobjvals: %s\n",objvalbuffer);
|
||||
syslog(LOG_DEBUG,"netobjascs: %s\n",objascbuffer);
|
||||
}
|
||||
/* get_client -------------------------------------------------------------- */
|
||||
/*
|
||||
* Purpose: Get a CLIENT* for making RPC calls to lockd on given host
|
||||
@ -382,7 +416,7 @@ nlm_test_1_svc(arg, rqstp)
|
||||
if (debug_level)
|
||||
log_from_addr("nlm_test", rqstp);
|
||||
|
||||
holder = testlock(&arg4, 0);
|
||||
holder = testlock(&arg4, arg->exclusive, 0);
|
||||
/*
|
||||
* Copy the cookie from the argument into the result. Note that this
|
||||
* is slightly hazardous, as the structure contains a pointer to a
|
||||
@ -422,7 +456,7 @@ nlm_test_msg_1_svc(arg, rqstp)
|
||||
if (debug_level)
|
||||
log_from_addr("nlm_test_msg", rqstp);
|
||||
|
||||
holder = testlock(&arg4, 0);
|
||||
holder = testlock(&arg4, arg->exclusive, 0);
|
||||
|
||||
res.cookie = arg->cookie;
|
||||
if (holder == NULL) {
|
||||
@ -867,8 +901,23 @@ nlm4_test_4_svc(arg, rqstp)
|
||||
|
||||
if (debug_level)
|
||||
log_from_addr("nlm4_test", rqstp);
|
||||
if (debug_level > 5) {
|
||||
syslog(LOG_DEBUG, "Locking arguments:\n");
|
||||
log_netobj(&(arg->cookie));
|
||||
syslog(LOG_DEBUG, "Alock arguments:\n");
|
||||
syslog(LOG_DEBUG, "Caller Name: %s\n",arg->alock.caller_name);
|
||||
syslog(LOG_DEBUG, "File Handle:\n");
|
||||
log_netobj(&(arg->alock.fh));
|
||||
syslog(LOG_DEBUG, "Owner Handle:\n");
|
||||
log_netobj(&(arg->alock.oh));
|
||||
syslog(LOG_DEBUG, "SVID: %d\n", arg->alock.svid);
|
||||
syslog(LOG_DEBUG, "Lock Offset: %d\n", arg->alock.l_offset);
|
||||
syslog(LOG_DEBUG, "Lock Length: %d\n", arg->alock.l_len);
|
||||
syslog(LOG_DEBUG, "Exclusive: %s\n",
|
||||
(arg->exclusive ? "true" : "false"));
|
||||
}
|
||||
|
||||
holder = testlock(&arg->alock, LOCK_V4);
|
||||
holder = testlock(&arg->alock, arg->exclusive, LOCK_V4);
|
||||
|
||||
/*
|
||||
* Copy the cookie from the argument into the result. Note that this
|
||||
@ -904,7 +953,7 @@ nlm4_test_msg_4_svc(arg, rqstp)
|
||||
if (debug_level)
|
||||
log_from_addr("nlm4_test_msg", rqstp);
|
||||
|
||||
holder = testlock(&arg->alock, LOCK_V4);
|
||||
holder = testlock(&arg->alock, arg->exclusive, LOCK_V4);
|
||||
|
||||
res.cookie = arg->cookie;
|
||||
if (holder == NULL) {
|
||||
@ -948,6 +997,23 @@ nlm4_lock_4_svc(arg, rqstp)
|
||||
|
||||
if (debug_level)
|
||||
log_from_addr("nlm4_lock", rqstp);
|
||||
if (debug_level > 5) {
|
||||
syslog(LOG_DEBUG, "Locking arguments:\n");
|
||||
log_netobj(&(arg->cookie));
|
||||
syslog(LOG_DEBUG, "Alock arguments:\n");
|
||||
syslog(LOG_DEBUG, "Caller Name: %s\n",arg->alock.caller_name);
|
||||
syslog(LOG_DEBUG, "File Handle:\n");
|
||||
log_netobj(&(arg->alock.fh));
|
||||
syslog(LOG_DEBUG, "Owner Handle:\n");
|
||||
log_netobj(&(arg->alock.oh));
|
||||
syslog(LOG_DEBUG, "SVID: %d\n", arg->alock.svid);
|
||||
syslog(LOG_DEBUG, "Lock Offset: %d\n", arg->alock.l_offset);
|
||||
syslog(LOG_DEBUG, "Lock Length: %d\n", arg->alock.l_len);
|
||||
syslog(LOG_DEBUG, "Block: %s\n", (arg->block ? "true" : "false"));
|
||||
syslog(LOG_DEBUG, "Exclusive: %s\n", (arg->exclusive ? "true" : "false"));
|
||||
syslog(LOG_DEBUG, "Reclaim: %s\n", (arg->reclaim ? "true" : "false"));
|
||||
syslog(LOG_DEBUG, "State num: %d\n", arg->state);
|
||||
}
|
||||
|
||||
/* copy cookie from arg to result. See comment in nlm_test_4() */
|
||||
res.cookie = arg->cookie;
|
||||
|
@ -54,7 +54,12 @@
|
||||
#include "lockd_lock.h"
|
||||
#include "lockd.h"
|
||||
|
||||
/* A set of utilities for managing file locking */
|
||||
/*
|
||||
* A set of utilities for managing file locking
|
||||
*
|
||||
* XXX: All locks are in a linked list, a better structure should be used
|
||||
* to improve search/access effeciency.
|
||||
*/
|
||||
LIST_HEAD(lcklst_head, file_lock);
|
||||
struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head);
|
||||
|
||||
@ -75,6 +80,7 @@ struct file_lock {
|
||||
|
||||
/* lock status */
|
||||
#define LKST_LOCKED 1 /* lock is locked */
|
||||
/* XXX: Is this flag file specific or lock specific? */
|
||||
#define LKST_WAITING 2 /* file is already locked by another host */
|
||||
#define LKST_PROCESSING 3 /* child is trying to aquire the lock */
|
||||
#define LKST_DYING 4 /* must dies when we get news from the child */
|
||||
@ -85,6 +91,8 @@ enum nlm_stats do_unlock __P((struct file_lock *));
|
||||
void send_granted __P((struct file_lock *, int));
|
||||
void siglock __P((void));
|
||||
void sigunlock __P((void));
|
||||
int regions_overlap __P((u_int64_t start1, u_int64_t len1, u_int64_t start2,
|
||||
u_int64_t len2));
|
||||
|
||||
/* list of hosts we monitor */
|
||||
LIST_HEAD(hostlst_head, host);
|
||||
@ -99,6 +107,35 @@ struct host {
|
||||
|
||||
void do_mon __P((char *));
|
||||
|
||||
/*
|
||||
* regions_overlap(): This function examines the two provided regions for overlap.
|
||||
* It is non-trivial because start+len *CAN* overflow a 64-bit unsigned integer
|
||||
* and NFS semantics are unspecified on this account.
|
||||
*/
|
||||
int
|
||||
regions_overlap(start1, len1, start2, len2)
|
||||
u_int64_t start1, len1, start2, len2;
|
||||
{
|
||||
int result;
|
||||
|
||||
/* XXX: Need to adjust checks to account for integer overflow */
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
/* Regions *must* overlap if they both extend to the end */
|
||||
result = TRUE;
|
||||
} else if (len1 == 0 && start2+len2 < start1) {
|
||||
/* Region 2 is completely to the left of Region 1 */
|
||||
result = FALSE;
|
||||
} else if (start1+len1 < start2 && len2 == 0) {
|
||||
/* Region 1 is completely to the left of region 2 */
|
||||
result = FALSE;
|
||||
} else if (start1 + len1 <= start2 || start2+len2 <= start1) {
|
||||
/* 1 is completely left of 2 or 2 is completely left of 1 */
|
||||
result = FALSE;
|
||||
} else {
|
||||
result = TRUE;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
/*
|
||||
* testlock(): inform the caller if the requested lock would be granted or not
|
||||
* returns NULL if lock would granted, or pointer to the current nlm4_holder
|
||||
@ -106,8 +143,9 @@ void do_mon __P((char *));
|
||||
*/
|
||||
|
||||
struct nlm4_holder *
|
||||
testlock(lock, flags)
|
||||
testlock(lock, exclusive, flags)
|
||||
struct nlm4_lock *lock;
|
||||
bool_t exclusive;
|
||||
int flags;
|
||||
{
|
||||
struct file_lock *fl;
|
||||
@ -122,18 +160,40 @@ testlock(lock, flags)
|
||||
fl = LIST_NEXT(fl, lcklst)) {
|
||||
if (fl->status != LKST_LOCKED)
|
||||
continue;
|
||||
/*
|
||||
* XXX: Could we possibly have identical filehandles
|
||||
* on different systems?
|
||||
* ie. Do we need to check more than just the filehandle?
|
||||
* ie. Could someone artificially create requests which are
|
||||
* security violations?
|
||||
*/
|
||||
if (memcmp(&fl->filehandle, &filehandle, sizeof(filehandle)))
|
||||
continue;
|
||||
/* got it ! */
|
||||
/* File handles match, look for lock region overlap */
|
||||
if (regions_overlap(lock->l_offset, lock->l_len,
|
||||
fl->client.l_offset, fl->client.l_len)) {
|
||||
syslog(LOG_DEBUG,
|
||||
"Region overlap found %llu : %llu -- %llu : %llu\n",
|
||||
lock->l_offset, lock->l_len,
|
||||
fl->client.l_offset,fl->client.l_len);
|
||||
/* Regions overlap. Now check for exclusivity. */
|
||||
if (exclusive || fl->client.exclusive) {
|
||||
/* Lock test must fail, regions are exclusive */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Continue looping through all locks */
|
||||
}
|
||||
sigunlock();
|
||||
if (fl == NULL) {
|
||||
syslog(LOG_DEBUG, "test for %s: no lock found",
|
||||
lock->caller_name);
|
||||
return NULL;
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "test for %s: found lock held by %s",
|
||||
lock->caller_name, fl->client_name);
|
||||
sigunlock();
|
||||
return (&fl->client);
|
||||
}
|
||||
/* not found */
|
||||
sigunlock();
|
||||
syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
/* Headers and function declarations for file-locking utilities */
|
||||
|
||||
struct nlm4_holder * testlock __P((struct nlm4_lock *, int));
|
||||
struct nlm4_holder * testlock __P((struct nlm4_lock *, int, int));
|
||||
|
||||
enum nlm_stats getlock __P((nlm4_lockargs *, struct svc_req *, int));
|
||||
enum nlm_stats unlock __P((nlm4_lock *, int));
|
||||
|
Loading…
Reference in New Issue
Block a user