]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/nfs/nfs4proc.c
NFSv4.1: Fix a race where CB_NOTIFY_LOCK fails to wake a waiter
[karo-tx-linux.git] / fs / nfs / nfs4proc.c
index c08c46a3b8cde00ef5aa40fae87ed2fce06faea1..93c1d735223861e845ffd256cb94ef4ef9a455ad 100644 (file)
@@ -275,6 +275,7 @@ const u32 nfs4_fs_locations_bitmap[3] = {
 static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
                struct nfs4_readdir_arg *readdir)
 {
+       unsigned int attrs = FATTR4_WORD0_FILEID | FATTR4_WORD0_TYPE;
        __be32 *start, *p;
 
        if (cookie > 2) {
@@ -305,8 +306,9 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
                memcpy(p, ".\0\0\0", 4);                        /* entry */
                p++;
                *p++ = xdr_one;                         /* bitmap length */
-               *p++ = htonl(FATTR4_WORD0_FILEID);             /* bitmap */
-               *p++ = htonl(8);              /* attribute buffer length */
+               *p++ = htonl(attrs);                           /* bitmap */
+               *p++ = htonl(12);             /* attribute buffer length */
+               *p++ = htonl(NF4DIR);
                p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry)));
        }
        
@@ -317,8 +319,9 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
        memcpy(p, "..\0\0", 4);                         /* entry */
        p++;
        *p++ = xdr_one;                         /* bitmap length */
-       *p++ = htonl(FATTR4_WORD0_FILEID);             /* bitmap */
-       *p++ = htonl(8);              /* attribute buffer length */
+       *p++ = htonl(attrs);                           /* bitmap */
+       *p++ = htonl(12);             /* attribute buffer length */
+       *p++ = htonl(NF4DIR);
        p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry->d_parent)));
 
        readdir->pgbase = (char *)p - (char *)start;
@@ -1034,11 +1037,11 @@ struct nfs4_opendata {
        struct nfs4_state *state;
        struct iattr attrs;
        unsigned long timestamp;
-       unsigned int rpc_done : 1;
-       unsigned int file_created : 1;
-       unsigned int is_recover : 1;
+       bool rpc_done;
+       bool file_created;
+       bool is_recover;
+       bool cancelled;
        int rpc_status;
-       int cancelled;
 };
 
 static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
@@ -1962,7 +1965,7 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
                nfs4_stateid_copy(&data->o_res.stateid, &data->c_res.stateid);
                nfs_confirm_seqid(&data->owner->so_seqid, 0);
                renew_lease(data->o_res.server, data->timestamp);
-               data->rpc_done = 1;
+               data->rpc_done = true;
        }
 }
 
@@ -1972,7 +1975,7 @@ static void nfs4_open_confirm_release(void *calldata)
        struct nfs4_state *state = NULL;
 
        /* If this request hasn't been cancelled, do nothing */
-       if (data->cancelled == 0)
+       if (!data->cancelled)
                goto out_free;
        /* In case of error, no cleanup! */
        if (!data->rpc_done)
@@ -2015,7 +2018,7 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
 
        nfs4_init_sequence(&data->c_arg.seq_args, &data->c_res.seq_res, 1);
        kref_get(&data->kref);
-       data->rpc_done = 0;
+       data->rpc_done = false;
        data->rpc_status = 0;
        data->timestamp = jiffies;
        if (data->is_recover)
@@ -2025,7 +2028,7 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
                return PTR_ERR(task);
        status = rpc_wait_for_completion_task(task);
        if (status != 0) {
-               data->cancelled = 1;
+               data->cancelled = true;
                smp_wmb();
        } else
                status = data->rpc_status;
@@ -2124,7 +2127,7 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
                if (!(data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM))
                        nfs_confirm_seqid(&data->owner->so_seqid, 0);
        }
-       data->rpc_done = 1;
+       data->rpc_done = true;
 }
 
 static void nfs4_open_release(void *calldata)
@@ -2133,7 +2136,7 @@ static void nfs4_open_release(void *calldata)
        struct nfs4_state *state = NULL;
 
        /* If this request hasn't been cancelled, do nothing */
-       if (data->cancelled == 0)
+       if (!data->cancelled)
                goto out_free;
        /* In case of error, no cleanup! */
        if (data->rpc_status != 0 || !data->rpc_done)
@@ -2179,20 +2182,20 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
 
        nfs4_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1);
        kref_get(&data->kref);
-       data->rpc_done = 0;
+       data->rpc_done = false;
        data->rpc_status = 0;
-       data->cancelled = 0;
-       data->is_recover = 0;
+       data->cancelled = false;
+       data->is_recover = false;
        if (isrecover) {
                nfs4_set_sequence_privileged(&o_arg->seq_args);
-               data->is_recover = 1;
+               data->is_recover = true;
        }
        task = rpc_run_task(&task_setup_data);
        if (IS_ERR(task))
                return PTR_ERR(task);
        status = rpc_wait_for_completion_task(task);
        if (status != 0) {
-               data->cancelled = 1;
+               data->cancelled = true;
                smp_wmb();
        } else
                status = data->rpc_status;
@@ -2233,7 +2236,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred,
                                int openflags)
 {
        struct nfs_access_entry cache;
-       u32 mask;
+       u32 mask, flags;
 
        /* access call failed or for some reason the server doesn't
         * support any access modes -- defer access call until later */
@@ -2247,16 +2250,20 @@ static int nfs4_opendata_access(struct rpc_cred *cred,
         */
        if (openflags & __FMODE_EXEC) {
                /* ONLY check for exec rights */
-               mask = MAY_EXEC;
+               if (S_ISDIR(state->inode->i_mode))
+                       mask = NFS4_ACCESS_LOOKUP;
+               else
+                       mask = NFS4_ACCESS_EXECUTE;
        } else if ((fmode & FMODE_READ) && !opendata->file_created)
-               mask = MAY_READ;
+               mask = NFS4_ACCESS_READ;
 
        cache.cred = cred;
        cache.jiffies = jiffies;
        nfs_access_set_mask(&cache, opendata->o_res.access_result);
        nfs_access_add_cache(state->inode, &cache);
 
-       if ((mask & ~cache.mask & (MAY_READ | MAY_EXEC)) == 0)
+       flags = NFS4_ACCESS_READ | NFS4_ACCESS_EXECUTE | NFS4_ACCESS_LOOKUP;
+       if ((mask & ~cache.mask & flags) == 0)
                return 0;
 
        return -EACCES;
@@ -2287,9 +2294,9 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
 
        if (o_arg->open_flags & O_CREAT) {
                if (o_arg->open_flags & O_EXCL)
-                       data->file_created = 1;
+                       data->file_created = true;
                else if (o_res->cinfo.before != o_res->cinfo.after)
-                       data->file_created = 1;
+                       data->file_created = true;
                if (data->file_created || dir->i_version != o_res->cinfo.after)
                        update_changeattr(dir, &o_res->cinfo,
                                        o_res->f_attr->time_start);
@@ -3802,6 +3809,54 @@ nfs4_proc_lookup_mountpoint(struct inode *dir, const struct qstr *name,
        return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
 }
 
+static int _nfs4_proc_lookupp(struct inode *inode,
+               struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+               struct nfs4_label *label)
+{
+       struct rpc_clnt *clnt = NFS_CLIENT(inode);
+       struct nfs_server *server = NFS_SERVER(inode);
+       int                    status;
+       struct nfs4_lookupp_arg args = {
+               .bitmask = server->attr_bitmask,
+               .fh = NFS_FH(inode),
+       };
+       struct nfs4_lookupp_res res = {
+               .server = server,
+               .fattr = fattr,
+               .label = label,
+               .fh = fhandle,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUPP],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+
+       args.bitmask = nfs4_bitmask(server, label);
+
+       nfs_fattr_init(fattr);
+
+       dprintk("NFS call  lookupp ino=0x%lx\n", inode->i_ino);
+       status = nfs4_call_sync(clnt, server, &msg, &args.seq_args,
+                               &res.seq_res, 0);
+       dprintk("NFS reply lookupp: %d\n", status);
+       return status;
+}
+
+static int nfs4_proc_lookupp(struct inode *inode, struct nfs_fh *fhandle,
+                            struct nfs_fattr *fattr, struct nfs4_label *label)
+{
+       struct nfs4_exception exception = { };
+       int err;
+       do {
+               err = _nfs4_proc_lookupp(inode, fhandle, fattr, label);
+               trace_nfs4_lookupp(inode, err);
+               err = nfs4_handle_exception(NFS_SERVER(inode), err,
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
 static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
 {
        struct nfs_server *server = NFS_SERVER(inode);
@@ -4272,7 +4327,7 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
 }
 
 static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
-               u64 cookie, struct page **pages, unsigned int count, int plus)
+               u64 cookie, struct page **pages, unsigned int count, bool plus)
 {
        struct inode            *dir = d_inode(dentry);
        struct nfs4_readdir_arg args = {
@@ -4310,7 +4365,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
 }
 
 static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
-               u64 cookie, struct page **pages, unsigned int count, int plus)
+               u64 cookie, struct page **pages, unsigned int count, bool plus)
 {
        struct nfs4_exception exception = { };
        int err;
@@ -6134,7 +6189,7 @@ static void nfs4_lock_release(void *calldata)
 
        dprintk("%s: begin!\n", __func__);
        nfs_free_seqid(data->arg.open_seqid);
-       if (data->cancelled != 0) {
+       if (data->cancelled) {
                struct rpc_task *task;
                task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
                                data->arg.lock_seqid);
@@ -6217,7 +6272,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
                        nfs4_handle_setlk_error(data->server, data->lsp,
                                        data->arg.new_lock_owner, ret);
        } else
-               data->cancelled = 1;
+               data->cancelled = true;
        rpc_put_task(task);
        dprintk("%s: done, ret = %d!\n", __func__, ret);
        trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
@@ -6440,7 +6495,7 @@ nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
                set_current_state(TASK_INTERRUPTIBLE);
                spin_unlock_irqrestore(&q->lock, flags);
 
-               freezable_schedule_timeout_interruptible(NFS4_LOCK_MAXTIMEOUT);
+               freezable_schedule_timeout(NFS4_LOCK_MAXTIMEOUT);
        }
 
        finish_wait(q, &wait);
@@ -7375,12 +7430,11 @@ static void nfs4_exchange_id_done(struct rpc_task *task, void *data)
        if (status == 0) {
                clp->cl_clientid = cdata->res.clientid;
                clp->cl_exchange_flags = cdata->res.flags;
+               clp->cl_seqid = cdata->res.seqid;
                /* Client ID is not confirmed */
-               if (!(cdata->res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
+               if (!(cdata->res.flags & EXCHGID4_FLAG_CONFIRMED_R))
                        clear_bit(NFS4_SESSION_ESTABLISHED,
-                       &clp->cl_session->session_state);
-                       clp->cl_seqid = cdata->res.seqid;
-               }
+                                 &clp->cl_session->session_state);
 
                kfree(clp->cl_serverowner);
                clp->cl_serverowner = cdata->res.server_owner;
@@ -9312,6 +9366,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .getattr        = nfs4_proc_getattr,
        .setattr        = nfs4_proc_setattr,
        .lookup         = nfs4_proc_lookup,
+       .lookupp        = nfs4_proc_lookupp,
        .access         = nfs4_proc_access,
        .readlink       = nfs4_proc_readlink,
        .create         = nfs4_proc_create,