NFSv4: Open state recovery must account for file permission changes
[pandora-kernel.git] / fs / nfs / nfs4state.c
index 39914be..c8b51f4 100644 (file)
@@ -935,7 +935,7 @@ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
                case -NFS4ERR_BAD_SEQID:
                        if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
                                return;
-                       printk(KERN_WARNING "NFS: v4 server returned a bad"
+                       pr_warn_ratelimited("NFS: v4 server returned a bad"
                                        " sequence-id error on an"
                                        " unconfirmed sequence %p!\n",
                                        seqid->sequence);
@@ -1075,6 +1075,33 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4
        nfs4_schedule_state_manager(clp);
 }
 
+void nfs_inode_find_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_open_context *ctx;
+       struct nfs4_state *state;
+       bool found = false;
+
+       spin_lock(&inode->i_lock);
+       list_for_each_entry(ctx, &nfsi->open_files, list) {
+               state = ctx->state;
+               if (state == NULL)
+                       continue;
+               if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
+                       continue;
+               if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
+                       continue;
+               nfs4_state_mark_reclaim_nograce(clp, state);
+               found = true;
+       }
+       spin_unlock(&inode->i_lock);
+       if (found)
+               nfs4_schedule_state_manager(clp);
+}
+
+
 static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
 {
        struct inode *inode = state->inode;
@@ -1156,12 +1183,17 @@ restart:
                if (status >= 0) {
                        status = nfs4_reclaim_locks(state, ops);
                        if (status >= 0) {
+                               spin_lock(&state->state_lock);
                                list_for_each_entry(lock, &state->lock_states, ls_locks) {
                                        if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
-                                               printk("%s: Lock reclaim failed!\n",
-                                                       __func__);
+                                               pr_warn_ratelimited("NFS: "
+                                                       "%s: Lock reclaim "
+                                                       "failed!\n", __func__);
                                }
+                               spin_unlock(&state->state_lock);
                                nfs4_put_open_state(state);
+                               clear_bit(NFS_STATE_RECLAIM_NOGRACE,
+                                       &state->flags);
                                goto restart;
                        }
                }
@@ -1171,6 +1203,9 @@ restart:
                                                __func__, status);
                        case -ENOENT:
                        case -ENOMEM:
+                       case -EACCES:
+                       case -EROFS:
+                       case -EIO:
                        case -ESTALE:
                                /*
                                 * Open state on this file cannot be recovered
@@ -1224,10 +1259,12 @@ static void nfs4_clear_open_state(struct nfs4_state *state)
        clear_bit(NFS_O_RDONLY_STATE, &state->flags);
        clear_bit(NFS_O_WRONLY_STATE, &state->flags);
        clear_bit(NFS_O_RDWR_STATE, &state->flags);
+       spin_lock(&state->state_lock);
        list_for_each_entry(lock, &state->lock_states, ls_locks) {
                lock->ls_seqid.flags = 0;
                lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
        }
+       spin_unlock(&state->state_lock);
 }
 
 static void nfs4_reset_seqids(struct nfs_server *server,
@@ -1350,12 +1387,14 @@ static void nfs4_warn_keyexpired(const char *s)
 static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
 {
        switch (error) {
+               case 0:
+                       break;
                case -NFS4ERR_CB_PATH_DOWN:
                        nfs_handle_cb_pathdown(clp);
-                       return 0;
+                       break;
                case -NFS4ERR_NO_GRACE:
                        nfs4_state_end_reclaim_reboot(clp);
-                       return 0;
+                       break;
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_LEASE_MOVED:
                        set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
@@ -1375,13 +1414,15 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
                case -NFS4ERR_SEQ_MISORDERED:
                        set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
                        /* Zero session reset errors */
-                       return 0;
+                       break;
                case -EKEYEXPIRED:
                        /* Nothing we can do */
                        nfs4_warn_keyexpired(clp->cl_hostname);
-                       return 0;
+                       break;
+               default:
+                       return error;
        }
-       return error;
+       return 0;
 }
 
 static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
@@ -1411,7 +1452,8 @@ restart:
                        if (status < 0) {
                                set_bit(ops->owner_flag_bit, &sp->so_flags);
                                nfs4_put_state_owner(sp);
-                               return nfs4_recovery_handle_error(clp, status);
+                               status = nfs4_recovery_handle_error(clp, status);
+                               return (status != 0) ? status : -EAGAIN;
                        }
 
                        nfs4_put_state_owner(sp);
@@ -1420,7 +1462,7 @@ restart:
                spin_unlock(&clp->cl_lock);
        }
        rcu_read_unlock();
-       return status;
+       return 0;
 }
 
 static int nfs4_check_lease(struct nfs_client *clp)
@@ -1428,7 +1470,7 @@ static int nfs4_check_lease(struct nfs_client *clp)
        struct rpc_cred *cred;
        const struct nfs4_state_maintenance_ops *ops =
                clp->cl_mvops->state_renewal_ops;
-       int status = -NFS4ERR_EXPIRED;
+       int status;
 
        /* Is the client already known to have an expired lease? */
        if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
@@ -1438,6 +1480,7 @@ static int nfs4_check_lease(struct nfs_client *clp)
        spin_unlock(&clp->cl_lock);
        if (cred == NULL) {
                cred = nfs4_get_setclientid_cred(clp);
+               status = -ENOKEY;
                if (cred == NULL)
                        goto out;
        }
@@ -1525,16 +1568,16 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
 {
        if (!flags)
                return;
-       else if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
+       if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
                nfs41_handle_server_reboot(clp);
-       else if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
+       if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
                            SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
                            SEQ4_STATUS_ADMIN_STATE_REVOKED |
                            SEQ4_STATUS_LEASE_MOVED))
                nfs41_handle_state_revoked(clp);
-       else if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
+       if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
                nfs41_handle_recallable_state_revoked(clp);
-       else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
+       if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
                            SEQ4_STATUS_BACKCHANNEL_FAULT |
                            SEQ4_STATUS_CB_PATH_DOWN_SESSION))
                nfs41_handle_cb_path_down(clp);
@@ -1546,8 +1589,18 @@ static int nfs4_reset_session(struct nfs_client *clp)
 
        nfs4_begin_drain_session(clp);
        status = nfs4_proc_destroy_session(clp->cl_session);
-       if (status && status != -NFS4ERR_BADSESSION &&
-           status != -NFS4ERR_DEADSESSION) {
+       switch (status) {
+       case 0:
+       case -NFS4ERR_BADSESSION:
+       case -NFS4ERR_DEADSESSION:
+               break;
+       case -NFS4ERR_BACK_CHAN_BUSY:
+       case -NFS4ERR_DELAY:
+               set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+               status = 0;
+               ssleep(1);
+               goto out;
+       default:
                status = nfs4_recovery_handle_error(clp, status);
                goto out;
        }
@@ -1662,10 +1715,10 @@ static void nfs4_state_manager(struct nfs_client *clp)
 
                if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
                        status = nfs4_check_lease(clp);
+                       if (status < 0)
+                               goto out_error;
                        if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                                continue;
-                       if (status < 0 && status != -NFS4ERR_CB_PATH_DOWN)
-                               goto out_error;
                }
 
                /* Initialize or reset the session */
@@ -1682,23 +1735,18 @@ static void nfs4_state_manager(struct nfs_client *clp)
                if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
                        status = nfs4_do_reclaim(clp,
                                clp->cl_mvops->reboot_recovery_ops);
-                       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
-                           test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
-                               continue;
-                       nfs4_state_end_reclaim_reboot(clp);
-                       if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
+                       if (status == -EAGAIN)
                                continue;
                        if (status < 0)
                                goto out_error;
+                       nfs4_state_end_reclaim_reboot(clp);
                }
 
                /* Now recover expired state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
                        status = nfs4_do_reclaim(clp,
                                clp->cl_mvops->nograce_recovery_ops);
-                       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
-                           test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) ||
-                           test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+                       if (status == -EAGAIN)
                                continue;
                        if (status < 0)
                                goto out_error;
@@ -1728,7 +1776,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
        } while (atomic_read(&clp->cl_count) > 1);
        return;
 out_error:
-       printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
+       pr_warn_ratelimited("NFS: state manager failed on NFSv4 server %s"
                        " with error %d\n", clp->cl_hostname, -status);
        nfs4_end_drain_session(clp);
        nfs4_clear_state_manager_bit(clp);