From: Trond Myklebust <Trond.Myklebust@netapp.com>

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/lockd/clntproc.c    |   26 ++++++++++---
 25-akpm/fs/nfs/file.c          |   80 ++++++++++++++++++++++++++++++++++++++---
 25-akpm/fs/nfs/nfs4proc.c      |   23 ++++++++++-
 25-akpm/include/linux/nfs_fs.h |    5 --
 4 files changed, 117 insertions(+), 17 deletions(-)

diff -puN fs/lockd/clntproc.c~nfs-flock fs/lockd/clntproc.c
--- 25/fs/lockd/clntproc.c~nfs-flock	2005-01-18 03:26:54.074623192 -0800
+++ 25-akpm/fs/lockd/clntproc.c	2005-01-18 03:26:54.082621976 -0800
@@ -516,6 +516,24 @@ static void nlmclnt_locks_init_private(s
 	fl->fl_ops = &nlmclnt_lock_ops;
 }
 
+static void do_vfs_lock(struct file_lock *fl)
+{
+	int res = 0;
+	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
+		case FL_POSIX:
+			res = posix_lock_file_wait(fl->fl_file, fl);
+			break;
+		case FL_FLOCK:
+			res = flock_lock_file_wait(fl->fl_file, fl);
+			break;
+		default:
+			BUG();
+	}
+	if (res < 0)
+		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
+				__FUNCTION__);
+}
+
 /*
  * LOCK: Try to create a lock
  *
@@ -564,9 +582,7 @@ nlmclnt_lock(struct nlm_rqst *req, struc
 		fl->fl_u.nfs_fl.state = host->h_state;
 		fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
 		fl->fl_flags |= FL_SLEEP;
-		if (posix_lock_file_wait(fl->fl_file, fl) < 0)
-				printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
-						__FUNCTION__);
+		do_vfs_lock(fl);
 	}
 	status = nlm_stat_to_errno(resp->status);
 out:
@@ -635,7 +651,7 @@ nlmclnt_unlock(struct nlm_rqst *req, str
 					nlmclnt_unlock_callback);
 		/* Hrmf... Do the unlock early since locks_remove_posix()
 		 * really expects us to free the lock synchronously */
-		posix_lock_file(fl->fl_file, fl);
+		do_vfs_lock(fl);
 		if (status < 0) {
 			nlmclnt_release_lockargs(req);
 			kfree(req);
@@ -648,7 +664,7 @@ nlmclnt_unlock(struct nlm_rqst *req, str
 	if (status < 0)
 		return status;
 
-	posix_lock_file(fl->fl_file, fl);
+	do_vfs_lock(fl);
 	if (resp->status == NLM_LCK_GRANTED)
 		return 0;
 
diff -puN fs/nfs/file.c~nfs-flock fs/nfs/file.c
--- 25/fs/nfs/file.c~nfs-flock	2005-01-18 03:26:54.075623040 -0800
+++ 25-akpm/fs/nfs/file.c	2005-01-18 03:27:27.986467808 -0800
@@ -44,6 +44,8 @@ static ssize_t nfs_file_write(struct kio
 static int  nfs_file_flush(struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry, int datasync);
 static int nfs_check_flags(int flags);
+static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl);
+static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl);
 
 struct file_operations nfs_file_operations = {
 	.llseek		= remote_llseek,
@@ -57,6 +59,7 @@ struct file_operations nfs_file_operatio
 	.release	= nfs_file_release,
 	.fsync		= nfs_fsync,
 	.lock		= nfs_lock,
+	.flock		= nfs_flock,
 	.sendfile	= nfs_file_sendfile,
 	.check_flags	= nfs_check_flags,
 };
@@ -312,6 +315,25 @@ static int do_getlk(struct file *filp, i
 	return status;
 }
 
+static int do_vfs_lock(struct file *file, struct file_lock *fl)
+{
+	int res = 0;
+	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
+		case FL_POSIX:
+			res = posix_lock_file_wait(file, fl);
+			break;
+		case FL_FLOCK:
+			res = flock_lock_file_wait(file, fl);
+			break;
+		default:
+			BUG();
+	}
+	if (res < 0)
+		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
+				__FUNCTION__);
+	return res;
+}
+
 static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode *inode = filp->f_mapping->host;
@@ -338,7 +360,7 @@ static int do_unlk(struct file *filp, in
 	if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
 		status = NFS_PROTO(inode)->lock(filp, cmd, fl);
 	else
-		status = posix_lock_file_wait(filp, fl);
+		status = do_vfs_lock(filp, fl);
 	unlock_kernel();
 	rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
 	return status;
@@ -377,9 +399,9 @@ static int do_setlk(struct file *filp, i
 		 * the process exits.
 		 */
 		if (status == -EINTR || status == -ERESTARTSYS)
-			posix_lock_file_wait(filp, fl);
+			do_vfs_lock(filp, fl);
 	} else
-		status = posix_lock_file_wait(filp, fl);
+		status = do_vfs_lock(filp, fl);
 	unlock_kernel();
 	if (status < 0)
 		goto out;
@@ -401,10 +423,10 @@ out:
 /*
  * Lock a (portion of) a file
  */
-int
-nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode * inode = filp->f_mapping->host;
+	struct file_lock **lockp;
 
 	dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
 			inode->i_sb->s_id, inode->i_ino,
@@ -427,6 +449,12 @@ nfs_lock(struct file *filp, int cmd, str
 	 */
 	if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX))
 		return -ENOLCK;
+	for(lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) {
+		if (!((*lockp)->fl_flags & FL_FLOCK))
+			continue;
+		if (fl->fl_owner == (*lockp)->fl_owner)
+			return -ENOLCK;
+	}
 
 	if (IS_GETLK(cmd))
 		return do_getlk(filp, cmd, fl);
@@ -434,3 +462,45 @@ nfs_lock(struct file *filp, int cmd, str
 		return do_unlk(filp, cmd, fl);
 	return do_setlk(filp, cmd, fl);
 }
+
+/*
+ * Lock a (portion of) a file
+ */
+static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
+{
+	struct inode * inode = filp->f_mapping->host;
+	struct file_lock **lockp;
+
+	dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n",
+			inode->i_sb->s_id, inode->i_ino,
+			fl->fl_type, fl->fl_flags);
+
+	if (!inode)
+		return -EINVAL;
+
+	/*
+	 * No BSD flocks over NFS allowed.
+	 * Note: we could try to fake a POSIX lock request here by
+	 * using ((u32) filp | 0x80000000) or some such as the pid.
+	 * Not sure whether that would be unique, though, or whether
+	 * that would break in other places.
+	 */
+	if (!(fl->fl_flags & FL_FLOCK))
+		return -ENOLCK;
+
+	/* We're simulating flock() locks using posix locks on the server */
+	fl->fl_owner = current->files;
+	fl->fl_start = 0;
+	fl->fl_end = OFFSET_MAX;
+
+	for(lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) {
+		if (!((*lockp)->fl_flags & FL_POSIX))
+			continue;
+		if (fl->fl_owner == (*lockp)->fl_owner)
+			return -ENOLCK;
+	}
+
+	if (fl->fl_type == F_UNLCK)
+		return do_unlk(filp, cmd, fl);
+	return do_setlk(filp, cmd, fl);
+}
diff -puN fs/nfs/nfs4proc.c~nfs-flock fs/nfs/nfs4proc.c
--- 25/fs/nfs/nfs4proc.c~nfs-flock	2005-01-18 03:26:54.077622736 -0800
+++ 25-akpm/fs/nfs/nfs4proc.c	2005-01-18 03:26:54.086621368 -0800
@@ -2361,6 +2361,25 @@ static int nfs4_proc_getlk(struct nfs4_s
 	return err;
 }
 
+static int do_vfs_lock(struct file *file, struct file_lock *fl)
+{
+	int res = 0;
+	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
+		case FL_POSIX:
+			res = posix_lock_file_wait(file, fl);
+			break;
+		case FL_FLOCK:
+			res = flock_lock_file_wait(file, fl);
+			break;
+		default:
+			BUG();
+	}
+	if (res < 0)
+		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
+				__FUNCTION__);
+	return res;
+}
+
 static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
 	struct inode *inode = state->inode;
@@ -2408,7 +2427,7 @@ static int _nfs4_proc_unlck(struct nfs4_
 out:
 	up(&state->lock_sema);
 	if (status == 0)
-		posix_lock_file(request->fl_file, request);
+		do_vfs_lock(request->fl_file, request);
 	up_read(&clp->cl_sem);
 	return status;
 }
@@ -2517,7 +2536,7 @@ static int _nfs4_proc_setlk(struct nfs4_
 	if (status == 0) {
 		/* Note: we always want to sleep here! */
 		request->fl_flags |= FL_SLEEP;
-		if (posix_lock_file_wait(request->fl_file, request) < 0)
+		if (do_vfs_lock(request->fl_file, request) < 0)
 			printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
 	}
 	up_read(&clp->cl_sem);
diff -puN include/linux/nfs_fs.h~nfs-flock include/linux/nfs_fs.h
--- 25/include/linux/nfs_fs.h~nfs-flock	2005-01-18 03:26:54.078622584 -0800
+++ 25-akpm/include/linux/nfs_fs.h	2005-01-18 03:26:54.086621368 -0800
@@ -357,11 +357,6 @@ extern struct dentry_operations nfs_dent
 extern struct inode_operations nfs_symlink_inode_operations;
 
 /*
- * linux/fs/nfs/locks.c
- */
-extern int nfs_lock(struct file *, int, struct file_lock *);
-
-/*
  * linux/fs/nfs/unlink.c
  */
 extern int  nfs_async_unlink(struct dentry *);
_