diff -u linux-2.5.15-flock/fs/locks.c linux-2.5.15-flock/fs/locks.c
--- linux-2.5.15-flock/fs/locks.c	Thu May 16 14:52:40 2002
+++ linux-2.5.15-flock/fs/locks.c	Tue May 21 00:17:14 2002
@@ -402,14 +402,6 @@
 	return 0;
 }
 
-/* Check if two locks overlap each other.
- */
-static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
-{
-	return ((fl1->fl_end >= fl2->fl_start) &&
-		(fl2->fl_end >= fl1->fl_start));
-}
-
 /*
  * Check whether two locks have the same owner
  * N.B. Do we need the test on PID as well as owner?
@@ -473,6 +465,8 @@
  */
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
+	if (fl->fl_type == F_UNLCK)
+		return;
 	list_add(&fl->fl_link, &file_lock_list);
 
 	/* insert into file's list */
@@ -546,60 +540,34 @@
 	_delete_lock(fl);
 }
 
-/* Determine if lock sys_fl blocks lock caller_fl. Common functionality
- * checks for shared/exclusive status of overlapping locks.
- */
-static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
-{
-	switch (caller_fl->fl_type) {
-	case F_RDLCK:
-		return (sys_fl->fl_type == F_WRLCK);
-
-	case F_WRLCK:
-		return (1);
-
-	default:
-		printk(KERN_ERR "locks_conflict(): impossible lock type - %d\n",
-		       caller_fl->fl_type);
-		break;
-	}
-	return (0);	/* This should never happen */
-}
-
-/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific
- * checking before calling the locks_conflict().
- */
-static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+/* Determine if lock sys_fl blocks lock caller. */
+static int posix_locks_conflict(struct file_lock *caller, struct file_lock *sys_fl)
 {
 	/* POSIX locks owned by the same process do not conflict with
 	 * each other.
 	 */
-	if (!(sys_fl->fl_flags & FL_POSIX) ||
-	    locks_same_owner(caller_fl, sys_fl))
-		return (0);
+	if (locks_same_owner(caller, sys_fl))
+		return 0;
 
 	/* Check whether they overlap */
-	if (!locks_overlap(caller_fl, sys_fl))
+	if ((caller->fl_end >= sys_fl->fl_start) && (sys_fl->fl_end >= caller->fl_start))
 		return 0;
 
-	return (locks_conflict(caller_fl, sys_fl));
+	return (caller->fl_type == F_WRLCK) || (sys_fl->fl_type == F_WRLCK);
 }
 
-/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific
- * checking before calling the locks_conflict().
- */
-static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+/* Determine if lock sys_fl blocks lock caller. */
+static int flock_locks_conflict(struct file_lock *caller, struct file_lock *sys_fl)
 {
 	/* FLOCK locks referring to the same filp do not conflict with
 	 * each other.
 	 */
-	if (!(sys_fl->fl_flags & FL_FLOCK) ||
-	    (caller_fl->fl_file == sys_fl->fl_file))
-		return (0);
-	if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
+	if (caller->fl_file == sys_fl->fl_file)
+		return 0;
+	if ((caller->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
 		return 0;
 
-	return (locks_conflict(caller_fl, sys_fl));
+	return (caller->fl_type == F_WRLCK) || (sys_fl->fl_type == F_WRLCK);
 }
 
 static int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, int timeout)
@@ -641,18 +609,19 @@
 struct file_lock *
 posix_test_lock(struct file *filp, struct file_lock *fl)
 {
-	struct file_lock *cfl;
+	struct file_lock **lockp, *cfl = NULL;
 
 	lock_kernel();
-	for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
-		if (!(cfl->fl_flags & FL_POSIX))
+	for_each_lock(filp->f_dentry->d_inode, lockp) {
+		cfl = *lockp;
+		if (!IS_POSIX(cfl))
 			continue;
 		if (posix_locks_conflict(cfl, fl))
 			break;
 	}
 	unlock_kernel();
 
-	return (cfl);
+	return cfl;
 }
 
 /* This function tests for deadlock condition before putting a process to
@@ -743,7 +712,7 @@
 	 * the proposed read/write.
 	 */
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (!(fl->fl_flags & FL_POSIX))
+		if (!IS_POSIX(fl))
 			continue;
 		if (fl->fl_start > new_fl->fl_end)
 			break;
@@ -768,8 +737,8 @@
 			goto repeat;
 		}
 	}
-	locks_free_lock(new_fl);
 	unlock_kernel();
+	locks_free_lock(new_fl);
 	return error;
 }
 
@@ -835,14 +804,14 @@
  * @caller: new lock
  * @fl: existing lock
  * @returns 0 to continue looking, 1 to stop looking and -1 to indicate
- * that we deleted the lock we were looking 
+ * that the current lock should be deleted.
  *
  * Attempt to merge these two locks.  Due to the heinous POSIX locking
  * semantics, we may end up having to split an existing lock into three
  * pieces, so we need an extra lock.
  */
-int posix_juggle_locks(struct file_lock *caller, struct file_lock *fl,
-		struct file_lock **before, struct file_lock *extra)
+static int posix_juggle_locks(struct file_lock *caller,
+		struct file_lock *fl, struct file_lock *extra)
 {
 	if (caller->fl_type == fl->fl_type) {
 		if (fl->fl_end < caller->fl_start - 1)
@@ -860,7 +829,6 @@
 		if (fl->fl_end > caller->fl_end) {
 			caller->fl_end = fl->fl_end;
 		}
-		locks_delete_lock(before);
 		return -1;
 	} else {
 		if (fl->fl_end < caller->fl_start)
@@ -872,7 +840,6 @@
 		if (caller->fl_start <= fl->fl_start) {
 			if (fl->fl_end <= caller->fl_end) {
 				/* We completely replace this lock.  Just delete it. */
-				locks_delete_lock(before);
 				return -1;
 			}
 			fl->fl_start = caller->fl_end + 1;
@@ -888,7 +855,7 @@
 			return 1;
 		}
 		
-		/* The new lock splits the old lock.  POSIX, we hatesss them */
+		/* The new lock splits the old lock.  POSIX, we hatesss it */
 		locks_copy_lock(extra, fl);
 		fl->fl_end = caller->fl_start - 1;
 		extra->fl_start = caller->fl_end + 1;
@@ -911,11 +878,9 @@
 
 int posix_lock_file(struct file *filp, struct file_lock *caller)
 {
-	struct file_lock *fl;
-	struct file_lock *extra;
-	struct file_lock **before;
+	struct file_lock *extra, **before;
 	struct inode *inode = filp->f_dentry->d_inode;
-	int error, found = 0;
+	int error, found = 0, result = 0;
 
 	/*
 	 * We may need an extra file_lock structure for this operation,
@@ -928,8 +893,8 @@
 
 	lock_kernel();
 	if (caller->fl_type != F_UNLCK) {
- repeat:
-		for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		for_each_lock(inode, before) {
+			struct file_lock *fl = *before;
 			if (!IS_POSIX(fl))
 				continue;
 			if (!posix_locks_conflict(caller, fl))
@@ -940,18 +905,15 @@
 			error = -EDEADLK;
 			if (posix_locks_deadlock(caller, fl))
 				goto out;
-
-			error = locks_block_on(fl, caller);
-			if (error != 0)
-				goto out;
-			goto repeat;
+			error = -EAGAIN;
+			locks_insert_block(fl, caller);
+			goto out;
   		}
   	}
 
 	for_each_lock(inode, before) {
-		int result;
 		struct file_lock *fl;
- again:
+ deleted:
 		fl = *before;
 		if (!IS_POSIX(fl))
 			continue;
@@ -962,11 +924,13 @@
 				continue;
 		}
 		found = 1;
-		result = posix_juggle_locks(caller, fl, before, extra);
-		if (result == -1)
-			goto again;
-		if (result == 1)
+		result = posix_juggle_locks(caller, fl, extra);
+		if (result == -1) {
+			locks_delete_lock(before);
+			goto deleted;
+		} else if (result == 1) {
 			break;
+		}
 	}
 
 	locks_insert_lock(before, caller);
@@ -1151,7 +1115,7 @@
  */
 int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
-	struct file_lock *fl, **before, **my_before = NULL;
+	struct file_lock *lock, **before, **my_before = NULL;
 	struct dentry *dentry;
 	struct inode *inode;
 	int error, rdlease_count = 0, wrlease_count = 0;
@@ -1172,11 +1136,10 @@
 		|| (atomic_read(&inode->i_count) > 1)))
 		return -EAGAIN;
 
-	before = &inode->i_flock;
-
 	lock_kernel();
 
-	while ((fl = *before) != NULL) {
+	for_each_lock(inode, before) {
+		struct file_lock *fl = *before;
 		if (fl->fl_flags != FL_LEASE)
 			break;
 		if (fl->fl_file == filp)
@@ -1185,7 +1148,6 @@
 			wrlease_count++;
 		else
 			rdlease_count++;
-		before = &fl->fl_next;
 	}
 
 	if ((arg == F_RDLCK && (wrlease_count > 0)) ||
@@ -1209,18 +1171,18 @@
 		goto out_unlock;
 	}
 
-	error = lease_alloc(filp, arg, &fl);
+	error = lease_alloc(filp, arg, &lock);
 	if (error)
 		goto out_unlock;
 
-	error = fasync_helper(fd, filp, 1, &fl->fl_fasync);
+	error = fasync_helper(fd, filp, 1, &lock->fl_fasync);
 	if (error < 0) {
-		locks_free_lock(fl);
+		locks_free_lock(lock);
 		goto out_unlock;
 	}
-	fl->fl_next = *before;
-	*before = fl;
-	list_add(&fl->fl_link, &file_lock_list);
+	lock->fl_next = *before;
+	*before = lock;
+	list_add(&lock->fl_link, &file_lock_list);
 	filp->f_owner.pid = current->pid;
 	filp->f_owner.uid = current->uid;
 	filp->f_owner.euid = current->euid;
@@ -1268,9 +1230,7 @@
 
 	for (;;) {
 		error = flock_lock_file(filp, lock);
-		if (!error)
-			goto out_putf;
-		if (cmd & LOCK_NB)
+		if ((error != -EAGAIN) || (cmd & LOCK_NB))
 			break;
 		error = wait_event_interruptible(lock->fl_wait, !lock->fl_next);
 		if (!error)
@@ -1282,7 +1242,9 @@
 		break;
 	}
 
-	locks_free_lock(lock);
+	if (error) {
+		locks_free_lock(lock);
+	}
 
 out_putf:
 	fput(filp);
@@ -1417,10 +1379,23 @@
 		if (error < 0)
 			goto out;
 	}
-	error = posix_lock_file(filp, file_lock);
+	for (;;) {
+		error = posix_lock_file(filp, file_lock);
+		if ((error != -EAGAIN) || (cmd == F_SETLK))
+			break;
+		error = wait_event_interruptible(lock->fl_wait, !lock->fl_next);
+		if (!error)
+			continue;
+
+		lock_kernel();
+		locks_delete_block(lock);
+		unlock_kernel();
+		break;
+	}
 
 out:
-	locks_free_lock(file_lock);
+	if (error)
+		locks_free_lock(file_lock);
 	return error;
 }
 
@@ -1543,44 +1518,38 @@
 	error = posix_lock_file(filp, file_lock);
 
 out:
-	locks_free_lock(file_lock);
+	if (error)
+		locks_free_lock(file_lock);
 	return error;
 }
 #endif /* BITS_PER_LONG == 32 */
 
 /*
  * This function is called when the file is being removed
- * from the task's fd array.
+ * from the task's fd array.  POSIX locks belonging to this task
+ * are deleted at this time.
  */
 void locks_remove_posix(struct file *filp, fl_owner_t owner)
 {
-	struct inode * inode = filp->f_dentry->d_inode;
-	struct file_lock *fl;
-	struct file_lock **before;
-
-	/*
-	 * For POSIX locks we free all locks on this file for the given task.
-	 */
-	if (!inode->i_flock) {
-		/*
-		 * Notice that something might be grabbing a lock right now.
-		 * Consider it as a race won by us - event is async, so even if
-		 * we miss the lock added we can trivially consider it as added
-		 * after we went through this call.
-		 */
-		return;
-	}
-	lock_kernel();
-	before = &inode->i_flock;
-	while ((fl = *before) != NULL) {
-		if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
-			locks_unlock_delete(before);
-			before = &inode->i_flock;
-			continue;
-		}
-		before = &fl->fl_next;
+	struct file_lock lock;
+	
+	lock.fl_type = F_UNLCK;
+	lock.fl_flags = FL_POSIX;
+	lock.fl_start = 0;
+	lock.fl_end = OFFSET_MAX;
+	lock.fl_owner = owner;
+	lock.fl_pid = current->pid;
+	lock.fl_file = filp;
+	lock.fl_notify = NULL;
+	lock.fl_insert = NULL;
+	lock.fl_remove = NULL;
+	
+	if (filp->f_op && filp->f_op->lock != NULL) {
+		filp->f_op->lock(filp, F_SETLK, &lock);
+		/* Ignore any error -- we must remove the locks anyway */
 	}
-	unlock_kernel();
+
+	posix_lock_file(filp, &lock);
 }
 
 /*
@@ -1588,25 +1557,19 @@
  */
 void locks_remove_flock(struct file *filp)
 {
-	struct inode * inode = filp->f_dentry->d_inode; 
-	struct file_lock *fl;
-	struct file_lock **before;
-
-	if (!inode->i_flock)
-		return;
-
-	lock_kernel();
-	before = &inode->i_flock;
-
-	while ((fl = *before) != NULL) {
-		if ((fl->fl_flags & (FL_FLOCK|FL_LEASE))
-		    && (fl->fl_file == filp)) {
-			locks_delete_lock(before);
-			continue;
- 		}
-		before = &fl->fl_next;
-	}
-	unlock_kernel();
+	struct file_lock lock;
+	
+	lock.fl_type = F_UNLCK;
+	lock.fl_flags = FL_FLOCK;
+	lock.fl_start = 0;
+	lock.fl_end = OFFSET_MAX;
+	lock.fl_pid = current->pid;
+	lock.fl_file = filp;
+	lock.fl_notify = NULL;
+	lock.fl_insert = NULL;
+	lock.fl_remove = NULL;
+	
+	flock_lock_file(filp, &lock);
 }
 
 /**
