NFS: Rename struct nfs4_client to struct nfs_client
[safe/jmp/linux-2.6] / fs / nfs / nfs4proc.c
index 62aed07..168f3ff 100644 (file)
@@ -64,9 +64,7 @@ static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinf
 static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
 static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
 static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp);
-extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
-extern struct rpc_procinfo nfs4_procedures[];
+static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
 
 /* Prevent leaks of NFSv4 errors into userland */
 int nfs4_map_errors(int err)
@@ -121,6 +119,25 @@ const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE
                        0
 };
 
+const u32 nfs4_fs_locations_bitmap[2] = {
+       FATTR4_WORD0_TYPE
+       | FATTR4_WORD0_CHANGE
+       | FATTR4_WORD0_SIZE
+       | FATTR4_WORD0_FSID
+       | FATTR4_WORD0_FILEID
+       | FATTR4_WORD0_FS_LOCATIONS,
+       FATTR4_WORD1_MODE
+       | FATTR4_WORD1_NUMLINKS
+       | FATTR4_WORD1_OWNER
+       | FATTR4_WORD1_OWNER_GROUP
+       | FATTR4_WORD1_RAWDEV
+       | FATTR4_WORD1_SPACE_USED
+       | FATTR4_WORD1_TIME_ACCESS
+       | FATTR4_WORD1_TIME_METADATA
+       | FATTR4_WORD1_TIME_MODIFY
+       | FATTR4_WORD1_MOUNTED_ON_FILEID
+};
+
 static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
                struct nfs4_readdir_arg *readdir)
 {
@@ -178,22 +195,22 @@ static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
 
 static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
 {
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
        spin_lock(&clp->cl_lock);
        if (time_before(clp->cl_last_renewal,timestamp))
                clp->cl_last_renewal = timestamp;
        spin_unlock(&clp->cl_lock);
 }
 
-static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo)
+static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_inode *nfsi = NFS_I(dir);
 
-       spin_lock(&inode->i_lock);
-       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
+       spin_lock(&dir->i_lock);
+       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
        if (cinfo->before == nfsi->change_attr && cinfo->atomic)
                nfsi->change_attr = cinfo->after;
-       spin_unlock(&inode->i_lock);
+       spin_unlock(&dir->i_lock);
 }
 
 struct nfs4_opendata {
@@ -336,7 +353,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
        if (!(data->f_attr.valid & NFS_ATTR_FATTR))
                goto out;
        inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
-       if (inode == NULL)
+       if (IS_ERR(inode))
                goto out;
        state = nfs4_get_open_state(inode, data->owner);
        if (state == NULL)
@@ -605,11 +622,14 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
        int status;
 
        atomic_inc(&data->count);
+       /*
+        * If rpc_run_task() ends up calling ->rpc_release(), we
+        * want to ensure that it takes the 'error' code path.
+        */
+       data->rpc_status = -ENOMEM;
        task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_confirm_ops, data);
-       if (IS_ERR(task)) {
-               nfs4_opendata_free(data);
+       if (IS_ERR(task))
                return PTR_ERR(task);
-       }
        status = nfs4_wait_for_completion_rpc_task(task);
        if (status != 0) {
                data->cancelled = 1;
@@ -708,11 +728,14 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
        int status;
 
        atomic_inc(&data->count);
+       /*
+        * If rpc_run_task() ends up calling ->rpc_release(), we
+        * want to ensure that it takes the 'error' code path.
+        */
+       data->rpc_status = -ENOMEM;
        task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_ops, data);
-       if (IS_ERR(task)) {
-               nfs4_opendata_free(data);
+       if (IS_ERR(task))
                return PTR_ERR(task);
-       }
        status = nfs4_wait_for_completion_rpc_task(task);
        if (status != 0) {
                data->cancelled = 1;
@@ -769,7 +792,7 @@ out:
 
 int nfs4_recover_expired_lease(struct nfs_server *server)
 {
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
 
        if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                nfs4_schedule_state_recovery(clp);
@@ -844,7 +867,7 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
 {
        struct nfs_delegation *delegation;
        struct nfs_server *server = NFS_SERVER(inode);
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs4_state_owner *sp = NULL;
        struct nfs4_state *state = NULL;
@@ -930,7 +953,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
        struct nfs_server       *server = NFS_SERVER(dir);
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
        struct nfs4_opendata *opendata;
        int                     status;
 
@@ -947,7 +970,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
        status = -ENOMEM;
        opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr);
        if (opendata == NULL)
-               goto err_put_state_owner;
+               goto err_release_rwsem;
 
        status = _nfs4_proc_open(opendata);
        if (status != 0)
@@ -966,11 +989,11 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
        return 0;
 err_opendata_free:
        nfs4_opendata_free(opendata);
+err_release_rwsem:
+       up_read(&clp->cl_sem);
 err_put_state_owner:
        nfs4_put_state_owner(sp);
 out_err:
-       /* Note: clp->cl_sem must be released before nfs4_put_open_state()! */
-       up_read(&clp->cl_sem);
        *res = NULL;
        return status;
 }
@@ -1212,7 +1235,7 @@ out:
        return status;
 }
 
-static void nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
+static int nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
 {
        struct file *filp;
 
@@ -1221,8 +1244,10 @@ static void nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, st
                struct nfs_open_context *ctx;
                ctx = (struct nfs_open_context *)filp->private_data;
                ctx->state = state;
-       } else
-               nfs4_close_state(state, nd->intent.open.flags);
+               return 0;
+       }
+       nfs4_close_state(state, nd->intent.open.flags);
+       return PTR_ERR(filp);
 }
 
 struct dentry *
@@ -1323,7 +1348,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
        return status;
 }
 
-static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
+int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
 {
        struct nfs4_exception exception = { };
        int err;
@@ -1435,6 +1460,50 @@ out:
        return nfs4_map_errors(status);
 }
 
+/*
+ * Get locations and (maybe) other attributes of a referral.
+ * Note that we'll actually follow the referral later when
+ * we detect fsid mismatch in inode revalidation
+ */
+static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle)
+{
+       int status = -ENOMEM;
+       struct page *page = NULL;
+       struct nfs4_fs_locations *locations = NULL;
+       struct dentry dentry = {};
+
+       page = alloc_page(GFP_KERNEL);
+       if (page == NULL)
+               goto out;
+       locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
+       if (locations == NULL)
+               goto out;
+
+       dentry.d_name.name = name->name;
+       dentry.d_name.len = name->len;
+       status = nfs4_proc_fs_locations(dir, &dentry, locations, page);
+       if (status != 0)
+               goto out;
+       /* Make sure server returned a different fsid for the referral */
+       if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
+               dprintk("%s: server did not return a different fsid for a referral at %s\n", __FUNCTION__, name->name);
+               status = -EIO;
+               goto out;
+       }
+
+       memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
+       fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
+       if (!fattr->mode)
+               fattr->mode = S_IFDIR;
+       memset(fhandle, 0, sizeof(struct nfs_fh));
+out:
+       if (page)
+               __free_page(page);
+       if (locations)
+               kfree(locations);
+       return status;
+}
+
 static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 {
        struct nfs4_getattr_arg args = {
@@ -1539,6 +1608,8 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
        
        dprintk("NFS call  lookup %s\n", name->name);
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       if (status == -NFS4ERR_MOVED)
+               status = nfs4_get_referral(dir, name, fattr, fhandle);
        dprintk("NFS reply lookup: %d\n", status);
        return status;
 }
@@ -1829,7 +1900,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                        nfs_setattr_update_inode(state->inode, sattr);
        }
        if (status == 0 && nd != NULL && (nd->flags & LOOKUP_OPEN))
-               nfs4_intent_set_file(nd, dentry, state);
+               status = nfs4_intent_set_file(nd, dentry, state);
        else
                nfs4_close_state(state, flags);
 out:
@@ -2000,7 +2071,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        if (!status) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, res.dir_attr);
-               nfs_refresh_inode(inode, res.fattr);
+               nfs_post_op_update_inode(inode, res.fattr);
        }
 
        return status;
@@ -2450,7 +2521,7 @@ static void nfs4_proc_commit_setup(struct nfs_write_data *data, int how)
  */
 static void nfs4_renew_done(struct rpc_task *task, void *data)
 {
-       struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
+       struct nfs_client *clp = (struct nfs_client *)task->tk_msg.rpc_argp;
        unsigned long timestamp = (unsigned long)data;
 
        if (task->tk_status < 0) {
@@ -2472,7 +2543,7 @@ static const struct rpc_call_ops nfs4_renew_ops = {
        .rpc_call_done = nfs4_renew_done,
 };
 
-int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred)
+int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
@@ -2484,7 +2555,7 @@ int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred)
                        &nfs4_renew_ops, (void *)jiffies);
 }
 
-int nfs4_proc_renew(struct nfs4_client *clp, struct rpc_cred *cred)
+int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
@@ -2597,7 +2668,7 @@ out:
        nfs4_set_cached_acl(inode, acl);
 }
 
-static inline ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
 {
        struct page *pages[NFS4ACL_MAXPAGES];
        struct nfs_getaclargs args = {
@@ -2650,6 +2721,19 @@ out_free:
        return ret;
 }
 
+static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+{
+       struct nfs4_exception exception = { };
+       ssize_t ret;
+       do {
+               ret = __nfs4_get_acl_uncached(inode, buf, buflen);
+               if (ret >= 0)
+                       break;
+               ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception);
+       } while (exception.retry);
+       return ret;
+}
+
 static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
 {
        struct nfs_server *server = NFS_SERVER(inode);
@@ -2666,7 +2750,7 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
        return nfs4_get_acl_uncached(inode, buf, buflen);
 }
 
-static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct page *pages[NFS4ACL_MAXPAGES];
@@ -2692,10 +2776,22 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
        return ret;
 }
 
+static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+{
+       struct nfs4_exception exception = { };
+       int err;
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               __nfs4_proc_set_acl(inode, buf, buflen),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
 static int
 nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
 {
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
 
        if (!clp || task->tk_status >= 0)
                return 0;
@@ -2732,7 +2828,7 @@ static int nfs4_wait_bit_interruptible(void *word)
        return 0;
 }
 
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
+static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
 {
        sigset_t oldset;
        int res;
@@ -2775,7 +2871,7 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
  */
 int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
 {
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
        int ret = errorcode;
 
        exception->retry = 0;
@@ -2802,7 +2898,7 @@ int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct
        return nfs4_map_errors(ret);
 }
 
-int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
+int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
 {
        nfs4_verifier sc_verifier;
        struct nfs4_setclientid setclientid = {
@@ -2849,8 +2945,7 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
        return status;
 }
 
-int
-nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred)
+static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
 {
        struct nfs_fsinfo fsinfo;
        struct rpc_message msg = {
@@ -2874,6 +2969,24 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred)
        return status;
 }
 
+int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       long timeout;
+       int err;
+       do {
+               err = _nfs4_proc_setclientid_confirm(clp, cred);
+               switch (err) {
+                       case 0:
+                               return err;
+                       case -NFS4ERR_RESOURCE:
+                               /* The IBM lawyers misread another document! */
+                       case -NFS4ERR_DELAY:
+                               err = nfs4_delay(clp->cl_rpcclient, &timeout);
+               }
+       } while (err == 0);
+       return err;
+}
+
 struct nfs4_delegreturndata {
        struct nfs4_delegreturnargs args;
        struct nfs4_delegreturnres res;
@@ -2942,10 +3055,8 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        data->rpc_status = 0;
 
        task = rpc_run_task(NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs4_delegreturn_ops, data);
-       if (IS_ERR(task)) {
-               nfs4_delegreturn_release(data);
+       if (IS_ERR(task))
                return PTR_ERR(task);
-       }
        status = nfs4_wait_for_completion_rpc_task(task);
        if (status == 0) {
                status = data->rpc_status;
@@ -2995,7 +3106,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
 {
        struct inode *inode = state->inode;
        struct nfs_server *server = NFS_SERVER(inode);
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs4_state;
        struct nfs_lockt_args arg = {
                .fh = NFS_FH(inode),
                .fl = request,
@@ -3058,9 +3169,6 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl)
                default:
                        BUG();
        }
-       if (res < 0)
-               printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
-                               __FUNCTION__);
        return res;
 }
 
@@ -3165,7 +3273,6 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
                struct nfs_seqid *seqid)
 {
        struct nfs4_unlockdata *data;
-       struct rpc_task *task;
 
        data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
        if (data == NULL) {
@@ -3173,12 +3280,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
                return ERR_PTR(-ENOMEM);
        }
 
-       /* Unlock _before_ we do the RPC call */
-       do_vfs_lock(fl->fl_file, fl);
-       task = rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
-       if (IS_ERR(task))
-               nfs4_locku_release_calldata(data);
-       return task;
+       return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
 }
 
 static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
@@ -3188,30 +3290,28 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
        struct rpc_task *task;
        int status = 0;
 
-       /* Is this a delegated lock? */
-       if (test_bit(NFS_DELEGATED_STATE, &state->flags))
-               goto out_unlock;
-       /* Is this open_owner holding any locks on the server? */
-       if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
-               goto out_unlock;
-
        status = nfs4_set_lock_state(state, request);
+       /* Unlock _before_ we do the RPC call */
+       request->fl_flags |= FL_EXISTS;
+       if (do_vfs_lock(request->fl_file, request) == -ENOENT)
+               goto out;
        if (status != 0)
-               goto out_unlock;
+               goto out;
+       /* Is this a delegated lock? */
+       if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+               goto out;
        lsp = request->fl_u.nfs4_fl.owner;
-       status = -ENOMEM;
        seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+       status = -ENOMEM;
        if (seqid == NULL)
-               goto out_unlock;
+               goto out;
        task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid);
        status = PTR_ERR(task);
        if (IS_ERR(task))
-               goto out_unlock;
+               goto out;
        status = nfs4_wait_for_completion_rpc_task(task);
        rpc_release_task(task);
-       return status;
-out_unlock:
-       do_vfs_lock(request->fl_file, request);
+out:
        return status;
 }
 
@@ -3359,10 +3459,8 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
                data->arg.reclaim = 1;
        task = rpc_run_task(NFS_CLIENT(state->inode), RPC_TASK_ASYNC,
                        &nfs4_lock_ops, data);
-       if (IS_ERR(task)) {
-               nfs4_lock_release(data);
+       if (IS_ERR(task))
                return PTR_ERR(task);
-       }
        ret = nfs4_wait_for_completion_rpc_task(task);
        if (ret == 0) {
                ret = data->rpc_status;
@@ -3381,10 +3479,10 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
        struct nfs4_exception exception = { };
        int err;
 
-       /* Cache the lock if possible... */
-       if (test_bit(NFS_DELEGATED_STATE, &state->flags))
-               return 0;
        do {
+               /* Cache the lock if possible... */
+               if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
+                       return 0;
                err = _nfs4_do_setlk(state, F_SETLK, request, 1);
                if (err != -NFS4ERR_DELAY)
                        break;
@@ -3403,6 +3501,8 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
        if (err != 0)
                return err;
        do {
+               if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
+                       return 0;
                err = _nfs4_do_setlk(state, F_SETLK, request, 0);
                if (err != -NFS4ERR_DELAY)
                        break;
@@ -3413,30 +3513,43 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
 
 static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
-       struct nfs4_client *clp = state->owner->so_client;
+       struct nfs_client *clp = state->owner->so_client;
+       unsigned char fl_flags = request->fl_flags;
        int status;
 
        /* Is this a delegated open? */
-       if (NFS_I(state->inode)->delegation_state != 0) {
-               /* Yes: cache locks! */
-               status = do_vfs_lock(request->fl_file, request);
-               /* ...but avoid races with delegation recall... */
-               if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags))
-                       return status;
-       }
-       down_read(&clp->cl_sem);
        status = nfs4_set_lock_state(state, request);
        if (status != 0)
                goto out;
+       request->fl_flags |= FL_ACCESS;
+       status = do_vfs_lock(request->fl_file, request);
+       if (status < 0)
+               goto out;
+       down_read(&clp->cl_sem);
+       if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+               struct nfs_inode *nfsi = NFS_I(state->inode);
+               /* Yes: cache locks! */
+               down_read(&nfsi->rwsem);
+               /* ...but avoid races with delegation recall... */
+               if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+                       request->fl_flags = fl_flags & ~FL_SLEEP;
+                       status = do_vfs_lock(request->fl_file, request);
+                       up_read(&nfsi->rwsem);
+                       goto out_unlock;
+               }
+               up_read(&nfsi->rwsem);
+       }
        status = _nfs4_do_setlk(state, cmd, request, 0);
        if (status != 0)
-               goto out;
+               goto out_unlock;
        /* Note: we always want to sleep here! */
-       request->fl_flags |= FL_SLEEP;
+       request->fl_flags = fl_flags | FL_SLEEP;
        if (do_vfs_lock(request->fl_file, request) < 0)
                printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
-out:
+out_unlock:
        up_read(&clp->cl_sem);
+out:
+       request->fl_flags = fl_flags;
        return status;
 }
 
@@ -3544,6 +3657,8 @@ ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen)
 {
        size_t len = strlen(XATTR_NAME_NFSV4_ACL) + 1;
 
+       if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
+               return 0;
        if (buf && buflen < len)
                return -ERANGE;
        if (buf)
@@ -3551,6 +3666,36 @@ ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen)
        return len;
 }
 
+int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry,
+               struct nfs4_fs_locations *fs_locations, struct page *page)
+{
+       struct nfs_server *server = NFS_SERVER(dir);
+       u32 bitmask[2] = {
+               [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
+               [1] = FATTR4_WORD1_MOUNTED_ON_FILEID,
+       };
+       struct nfs4_fs_locations_arg args = {
+               .dir_fh = NFS_FH(dir),
+               .name = &dentry->d_name,
+               .page = page,
+               .bitmask = bitmask,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
+               .rpc_argp = &args,
+               .rpc_resp = fs_locations,
+       };
+       int status;
+
+       dprintk("%s: start\n", __FUNCTION__);
+       fs_locations->fattr.valid = 0;
+       fs_locations->server = server;
+       fs_locations->nlocations = 0;
+       status = rpc_call_sync(server->client, &msg, 0);
+       dprintk("%s: returned status = %d\n", __FUNCTION__, status);
+       return status;
+}
+
 struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
        .recover_open   = nfs4_open_reclaim,
        .recover_lock   = nfs4_lock_reclaim,