Commit 2c485f63 authored by Trond Myklebust's avatar Trond Myklebust

NFS: Add emulation of BSD flock() in terms of POSIX locks on the server

 This makes for an interesting situation in which programs compiled
 to use flock() and running on the server will not see the locks that
 are set by the clients. The clients will, however, see both POSIX and
 flock() locks set by other clients.

 It is the best you can do, given the limitations of the protocol.
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent d63b8289
......@@ -516,6 +516,24 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho
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, struct file_lock *fl)
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, struct file_lock *fl)
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, struct file_lock *fl)
if (status < 0)
return status;
posix_lock_file(fl->fl_file, fl);
do_vfs_lock(fl);
if (resp->status == NLM_LCK_GRANTED)
return 0;
......
......@@ -44,6 +44,8 @@ static ssize_t nfs_file_write(struct kiocb *, const char __user *, size_t, loff_
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_operations = {
.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, int cmd, struct file_lock *fl)
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, int cmd, struct file_lock *fl)
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, int cmd, struct file_lock *fl)
* 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,8 +423,7 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
/*
* 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;
......@@ -418,6 +439,27 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
return -ENOLCK;
if (IS_GETLK(cmd))
return do_getlk(filp, cmd, fl);
if (fl->fl_type == F_UNLCK)
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;
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
......@@ -425,11 +467,14 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
* Not sure whether that would be unique, though, or whether
* that would break in other places.
*/
if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX))
if (!(fl->fl_flags & FL_FLOCK))
return -ENOLCK;
if (IS_GETLK(cmd))
return do_getlk(filp, cmd, fl);
/* We're simulating flock() locks using posix locks on the server */
fl->fl_owner = (fl_owner_t)filp;
fl->fl_start = 0;
fl->fl_end = OFFSET_MAX;
if (fl->fl_type == F_UNLCK)
return do_unlk(filp, cmd, fl);
return do_setlk(filp, cmd, fl);
......
......@@ -2364,6 +2364,25 @@ static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *
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;
......@@ -2411,7 +2430,7 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock
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;
}
......@@ -2520,7 +2539,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
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);
......
......@@ -350,11 +350,6 @@ extern struct dentry_operations nfs_dentry_operations;
*/
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
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment