Merge branch 'for-2.6.32' of git://linux-nfs.org/~bfields/linux
* 'for-2.6.32' of git://linux-nfs.org/~bfields/linux: (68 commits) nfsd4: nfsv4 clients should cross mountpoints nfsd: revise 4.1 status documentation sunrpc/cache: avoid variable over-loading in cache_defer_req sunrpc/cache: use list_del_init for the list_head entries in cache_deferred_req nfsd: return success for non-NFS4 nfs4_state_start nfsd41: Refactor create_client() nfsd41: modify nfsd4.1 backchannel to use new xprt class nfsd41: Backchannel: Implement cb_recall over NFSv4.1 nfsd41: Backchannel: cb_sequence callback nfsd41: Backchannel: Setup sequence information nfsd41: Backchannel: Server backchannel RPC wait queue nfsd41: Backchannel: Add sequence arguments to callback RPC arguments nfsd41: Backchannel: callback infrastructure nfsd4: use common rpc_cred for all callbacks nfsd4: allow nfs4 state startup to fail SUNRPC: Defer the auth_gss upcall when the RPC call is asynchronous nfsd4: fix null dereference creating nfsv4 callback client nfsd4: fix whitespace in NFSPROC4_CLNT_CB_NULL definition nfsd41: sunrpc: add new xprt class for nfsv4.1 backchannel sunrpc/cache: simplify cache_fresh_locked and cache_fresh_unlocked. ...
This commit is contained in:
@@ -1341,6 +1341,8 @@ exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
if (rv)
|
||||
goto out;
|
||||
rv = check_nfsd_access(exp, rqstp);
|
||||
if (rv)
|
||||
fh_put(fhp);
|
||||
out:
|
||||
exp_put(exp);
|
||||
return rv;
|
||||
|
@@ -814,17 +814,6 @@ encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name,
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p,
|
||||
struct svc_fh *fhp)
|
||||
{
|
||||
p = encode_post_op_attr(cd->rqstp, p, fhp);
|
||||
*p++ = xdr_one; /* yes, a file handle follows */
|
||||
p = encode_fh(p, fhp);
|
||||
fh_put(fhp);
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
|
||||
const char *name, int namlen)
|
||||
@@ -836,29 +825,54 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
|
||||
dparent = cd->fh.fh_dentry;
|
||||
exp = cd->fh.fh_export;
|
||||
|
||||
fh_init(fhp, NFS3_FHSIZE);
|
||||
if (isdotent(name, namlen)) {
|
||||
if (namlen == 2) {
|
||||
dchild = dget_parent(dparent);
|
||||
if (dchild == dparent) {
|
||||
/* filesystem root - cannot return filehandle for ".." */
|
||||
dput(dchild);
|
||||
return 1;
|
||||
return -ENOENT;
|
||||
}
|
||||
} else
|
||||
dchild = dget(dparent);
|
||||
} else
|
||||
dchild = lookup_one_len(name, dparent, namlen);
|
||||
if (IS_ERR(dchild))
|
||||
return 1;
|
||||
if (d_mountpoint(dchild) ||
|
||||
fh_compose(fhp, exp, dchild, &cd->fh) != 0 ||
|
||||
!dchild->d_inode)
|
||||
rv = 1;
|
||||
return -ENOENT;
|
||||
rv = -ENOENT;
|
||||
if (d_mountpoint(dchild))
|
||||
goto out;
|
||||
rv = fh_compose(fhp, exp, dchild, &cd->fh);
|
||||
if (rv)
|
||||
goto out;
|
||||
if (!dchild->d_inode)
|
||||
goto out;
|
||||
rv = 0;
|
||||
out:
|
||||
dput(dchild);
|
||||
return rv;
|
||||
}
|
||||
|
||||
__be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen)
|
||||
{
|
||||
struct svc_fh fh;
|
||||
int err;
|
||||
|
||||
fh_init(&fh, NFS3_FHSIZE);
|
||||
err = compose_entry_fh(cd, &fh, name, namlen);
|
||||
if (err) {
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
goto out;
|
||||
}
|
||||
p = encode_post_op_attr(cd->rqstp, p, &fh);
|
||||
*p++ = xdr_one; /* yes, a file handle follows */
|
||||
p = encode_fh(p, &fh);
|
||||
out:
|
||||
fh_put(&fh);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a directory entry. This one works for both normal readdir
|
||||
* and readdirplus.
|
||||
@@ -929,16 +943,8 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
|
||||
|
||||
p = encode_entry_baggage(cd, p, name, namlen, ino);
|
||||
|
||||
/* throw in readdirplus baggage */
|
||||
if (plus) {
|
||||
struct svc_fh fh;
|
||||
|
||||
if (compose_entry_fh(cd, &fh, name, namlen) > 0) {
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
} else
|
||||
p = encode_entryplus_baggage(cd, p, &fh);
|
||||
}
|
||||
if (plus)
|
||||
p = encode_entryplus_baggage(cd, p, name, namlen);
|
||||
num_entry_words = p - cd->buffer;
|
||||
} else if (cd->rqstp->rq_respages[pn+1] != NULL) {
|
||||
/* temporarily encode entry into next page, then move back to
|
||||
@@ -951,17 +957,8 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
|
||||
|
||||
p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
|
||||
|
||||
/* throw in readdirplus baggage */
|
||||
if (plus) {
|
||||
struct svc_fh fh;
|
||||
|
||||
if (compose_entry_fh(cd, &fh, name, namlen) > 0) {
|
||||
/* zero out the filehandle */
|
||||
*p1++ = 0;
|
||||
*p1++ = 0;
|
||||
} else
|
||||
p1 = encode_entryplus_baggage(cd, p1, &fh);
|
||||
}
|
||||
if (plus)
|
||||
p = encode_entryplus_baggage(cd, p1, name, namlen);
|
||||
|
||||
/* determine entry word length and lengths to go in pages */
|
||||
num_entry_words = p1 - tmp;
|
||||
|
@@ -321,7 +321,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
|
||||
deny = ~pas.group & pas.other;
|
||||
if (deny) {
|
||||
ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
|
||||
ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
|
||||
ace->flag = eflag;
|
||||
ace->access_mask = deny_mask_from_posix(deny, flags);
|
||||
ace->whotype = NFS4_ACL_WHO_GROUP;
|
||||
ace++;
|
||||
@@ -335,7 +335,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
|
||||
if (deny) {
|
||||
ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
|
||||
ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
|
||||
ace->access_mask = mask_from_posix(deny, flags);
|
||||
ace->access_mask = deny_mask_from_posix(deny, flags);
|
||||
ace->whotype = NFS4_ACL_WHO_NAMED;
|
||||
ace->who = pa->e_id;
|
||||
ace++;
|
||||
|
@@ -43,25 +43,30 @@
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
#include <linux/nfsd/nfsd.h>
|
||||
#include <linux/nfsd/state.h>
|
||||
#include <linux/sunrpc/sched.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/sunrpc/xprtsock.h>
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
||||
|
||||
#define NFSPROC4_CB_NULL 0
|
||||
#define NFSPROC4_CB_COMPOUND 1
|
||||
#define NFS4_STATEID_SIZE 16
|
||||
|
||||
/* Index of predefined Linux callback client operations */
|
||||
|
||||
enum {
|
||||
NFSPROC4_CLNT_CB_NULL = 0,
|
||||
NFSPROC4_CLNT_CB_NULL = 0,
|
||||
NFSPROC4_CLNT_CB_RECALL,
|
||||
NFSPROC4_CLNT_CB_SEQUENCE,
|
||||
};
|
||||
|
||||
enum nfs_cb_opnum4 {
|
||||
OP_CB_RECALL = 4,
|
||||
OP_CB_SEQUENCE = 11,
|
||||
};
|
||||
|
||||
#define NFS4_MAXTAGLEN 20
|
||||
@@ -70,17 +75,29 @@ enum nfs_cb_opnum4 {
|
||||
#define NFS4_dec_cb_null_sz 0
|
||||
#define cb_compound_enc_hdr_sz 4
|
||||
#define cb_compound_dec_hdr_sz (3 + (NFS4_MAXTAGLEN >> 2))
|
||||
#define sessionid_sz (NFS4_MAX_SESSIONID_LEN >> 2)
|
||||
#define cb_sequence_enc_sz (sessionid_sz + 4 + \
|
||||
1 /* no referring calls list yet */)
|
||||
#define cb_sequence_dec_sz (op_dec_sz + sessionid_sz + 4)
|
||||
|
||||
#define op_enc_sz 1
|
||||
#define op_dec_sz 2
|
||||
#define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2))
|
||||
#define enc_stateid_sz (NFS4_STATEID_SIZE >> 2)
|
||||
#define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \
|
||||
cb_sequence_enc_sz + \
|
||||
1 + enc_stateid_sz + \
|
||||
enc_nfs4_fh_sz)
|
||||
|
||||
#define NFS4_dec_cb_recall_sz (cb_compound_dec_hdr_sz + \
|
||||
cb_sequence_dec_sz + \
|
||||
op_dec_sz)
|
||||
|
||||
struct nfs4_rpc_args {
|
||||
void *args_op;
|
||||
struct nfsd4_cb_sequence args_seq;
|
||||
};
|
||||
|
||||
/*
|
||||
* Generic encode routines from fs/nfs/nfs4xdr.c
|
||||
*/
|
||||
@@ -137,11 +154,13 @@ xdr_error: \
|
||||
} while (0)
|
||||
|
||||
struct nfs4_cb_compound_hdr {
|
||||
int status;
|
||||
u32 ident;
|
||||
/* args */
|
||||
u32 ident; /* minorversion 0 only */
|
||||
u32 nops;
|
||||
__be32 *nops_p;
|
||||
u32 minorversion;
|
||||
/* res */
|
||||
int status;
|
||||
u32 taglen;
|
||||
char *tag;
|
||||
};
|
||||
@@ -238,6 +257,27 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
static void
|
||||
encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
|
||||
struct nfs4_cb_compound_hdr *hdr)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
if (hdr->minorversion == 0)
|
||||
return;
|
||||
|
||||
RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);
|
||||
|
||||
WRITE32(OP_CB_SEQUENCE);
|
||||
WRITEMEM(args->cbs_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
|
||||
WRITE32(args->cbs_clp->cl_cb_seq_nr);
|
||||
WRITE32(0); /* slotid, always 0 */
|
||||
WRITE32(0); /* highest slotid always 0 */
|
||||
WRITE32(0); /* cachethis always 0 */
|
||||
WRITE32(0); /* FIXME: support referring_call_lists */
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
|
||||
{
|
||||
@@ -249,15 +289,19 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_delegation *args)
|
||||
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
|
||||
struct nfs4_rpc_args *rpc_args)
|
||||
{
|
||||
struct xdr_stream xdr;
|
||||
struct nfs4_delegation *args = rpc_args->args_op;
|
||||
struct nfs4_cb_compound_hdr hdr = {
|
||||
.ident = args->dl_ident,
|
||||
.minorversion = rpc_args->args_seq.cbs_minorversion,
|
||||
};
|
||||
|
||||
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
|
||||
encode_cb_compound_hdr(&xdr, &hdr);
|
||||
encode_cb_sequence(&xdr, &rpc_args->args_seq, &hdr);
|
||||
encode_cb_recall(&xdr, args, &hdr);
|
||||
encode_cb_nops(&hdr);
|
||||
return 0;
|
||||
@@ -299,6 +343,57 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Our current back channel implmentation supports a single backchannel
|
||||
* with a single slot.
|
||||
*/
|
||||
static int
|
||||
decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
|
||||
struct rpc_rqst *rqstp)
|
||||
{
|
||||
struct nfs4_sessionid id;
|
||||
int status;
|
||||
u32 dummy;
|
||||
__be32 *p;
|
||||
|
||||
if (res->cbs_minorversion == 0)
|
||||
return 0;
|
||||
|
||||
status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* If the server returns different values for sessionID, slotID or
|
||||
* sequence number, the server is looney tunes.
|
||||
*/
|
||||
status = -ESERVERFAULT;
|
||||
|
||||
READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
|
||||
memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
|
||||
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
|
||||
if (memcmp(id.data, res->cbs_clp->cl_sessionid.data,
|
||||
NFS4_MAX_SESSIONID_LEN)) {
|
||||
dprintk("%s Invalid session id\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
READ32(dummy);
|
||||
if (dummy != res->cbs_clp->cl_cb_seq_nr) {
|
||||
dprintk("%s Invalid sequence number\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
READ32(dummy); /* slotid must be 0 */
|
||||
if (dummy != 0) {
|
||||
dprintk("%s Invalid slotid\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
/* FIXME: process highest slotid and target highest slotid */
|
||||
status = 0;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
|
||||
{
|
||||
@@ -306,7 +401,8 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p)
|
||||
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
|
||||
struct nfsd4_cb_sequence *seq)
|
||||
{
|
||||
struct xdr_stream xdr;
|
||||
struct nfs4_cb_compound_hdr hdr;
|
||||
@@ -316,6 +412,11 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p)
|
||||
status = decode_cb_compound_hdr(&xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
if (seq) {
|
||||
status = decode_cb_sequence(&xdr, seq, rqstp);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
|
||||
out:
|
||||
return status;
|
||||
@@ -377,16 +478,15 @@ static int max_cb_time(void)
|
||||
|
||||
int setup_callback_client(struct nfs4_client *clp)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
|
||||
struct rpc_timeout timeparms = {
|
||||
.to_initval = max_cb_time(),
|
||||
.to_retries = 0,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.protocol = IPPROTO_TCP,
|
||||
.address = (struct sockaddr *)&addr,
|
||||
.addrsize = sizeof(addr),
|
||||
.protocol = XPRT_TRANSPORT_TCP,
|
||||
.address = (struct sockaddr *) &cb->cb_addr,
|
||||
.addrsize = cb->cb_addrlen,
|
||||
.timeout = &timeparms,
|
||||
.program = &cb_program,
|
||||
.prognumber = cb->cb_prog,
|
||||
@@ -399,13 +499,10 @@ int setup_callback_client(struct nfs4_client *clp)
|
||||
|
||||
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
|
||||
return -EINVAL;
|
||||
|
||||
/* Initialize address */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(cb->cb_port);
|
||||
addr.sin_addr.s_addr = htonl(cb->cb_addr);
|
||||
|
||||
if (cb->cb_minorversion) {
|
||||
args.bc_xprt = clp->cl_cb_xprt;
|
||||
args.protocol = XPRT_TRANSPORT_BC_TCP;
|
||||
}
|
||||
/* Create RPC client */
|
||||
client = rpc_create(&args);
|
||||
if (IS_ERR(client)) {
|
||||
@@ -439,42 +536,29 @@ static const struct rpc_call_ops nfsd4_cb_probe_ops = {
|
||||
.rpc_call_done = nfsd4_cb_probe_done,
|
||||
};
|
||||
|
||||
static struct rpc_cred *lookup_cb_cred(struct nfs4_cb_conn *cb)
|
||||
{
|
||||
struct auth_cred acred = {
|
||||
.machine_cred = 1
|
||||
};
|
||||
static struct rpc_cred *callback_cred;
|
||||
|
||||
/*
|
||||
* Note in the gss case this doesn't actually have to wait for a
|
||||
* gss upcall (or any calls to the client); this just creates a
|
||||
* non-uptodate cred which the rpc state machine will fill in with
|
||||
* a refresh_upcall later.
|
||||
*/
|
||||
return rpcauth_lookup_credcache(cb->cb_client->cl_auth, &acred,
|
||||
RPCAUTH_LOOKUP_NEW);
|
||||
int set_callback_cred(void)
|
||||
{
|
||||
callback_cred = rpc_lookup_machine_cred();
|
||||
if (!callback_cred)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void do_probe_callback(struct nfs4_client *clp)
|
||||
{
|
||||
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
|
||||
.rpc_argp = clp,
|
||||
.rpc_cred = callback_cred
|
||||
};
|
||||
struct rpc_cred *cred;
|
||||
int status;
|
||||
|
||||
cred = lookup_cb_cred(cb);
|
||||
if (IS_ERR(cred)) {
|
||||
status = PTR_ERR(cred);
|
||||
goto out;
|
||||
}
|
||||
cb->cb_cred = cred;
|
||||
msg.rpc_cred = cb->cb_cred;
|
||||
status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_SOFT,
|
||||
&nfsd4_cb_probe_ops, (void *)clp);
|
||||
out:
|
||||
if (status) {
|
||||
warn_no_callback_path(clp, status);
|
||||
put_nfs4_client(clp);
|
||||
@@ -503,11 +587,95 @@ nfsd4_probe_callback(struct nfs4_client *clp)
|
||||
do_probe_callback(clp);
|
||||
}
|
||||
|
||||
/*
|
||||
* There's currently a single callback channel slot.
|
||||
* If the slot is available, then mark it busy. Otherwise, set the
|
||||
* thread for sleeping on the callback RPC wait queue.
|
||||
*/
|
||||
static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
|
||||
struct rpc_task *task)
|
||||
{
|
||||
struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
|
||||
u32 *ptr = (u32 *)clp->cl_sessionid.data;
|
||||
int status = 0;
|
||||
|
||||
dprintk("%s: %u:%u:%u:%u\n", __func__,
|
||||
ptr[0], ptr[1], ptr[2], ptr[3]);
|
||||
|
||||
if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
|
||||
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
|
||||
dprintk("%s slot is busy\n", __func__);
|
||||
status = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll need the clp during XDR encoding and decoding,
|
||||
* and the sequence during decoding to verify the reply
|
||||
*/
|
||||
args->args_seq.cbs_clp = clp;
|
||||
task->tk_msg.rpc_resp = &args->args_seq;
|
||||
|
||||
out:
|
||||
dprintk("%s status=%d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: cb_sequence should support referring call lists, cachethis, multiple
|
||||
* slots, and mark callback channel down on communication errors.
|
||||
*/
|
||||
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
|
||||
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
|
||||
int status = 0;
|
||||
|
||||
args->args_seq.cbs_minorversion = minorversion;
|
||||
if (minorversion) {
|
||||
status = nfsd41_cb_setup_sequence(clp, task);
|
||||
if (status) {
|
||||
if (status != -EAGAIN) {
|
||||
/* terminate rpc task */
|
||||
task->tk_status = status;
|
||||
task->tk_action = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
rpc_call_start(task);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
|
||||
dprintk("%s: minorversion=%d\n", __func__,
|
||||
clp->cl_cb_conn.cb_minorversion);
|
||||
|
||||
if (clp->cl_cb_conn.cb_minorversion) {
|
||||
/* No need for lock, access serialized in nfsd4_cb_prepare */
|
||||
++clp->cl_cb_seq_nr;
|
||||
clear_bit(0, &clp->cl_cb_slot_busy);
|
||||
rpc_wake_up_next(&clp->cl_cb_waitq);
|
||||
dprintk("%s: freed slot, new seqid=%d\n", __func__,
|
||||
clp->cl_cb_seq_nr);
|
||||
|
||||
/* We're done looking into the sequence information */
|
||||
task->tk_msg.rpc_resp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
|
||||
nfsd4_cb_done(task, calldata);
|
||||
|
||||
switch (task->tk_status) {
|
||||
case -EIO:
|
||||
/* Network partition? */
|
||||
@@ -520,16 +688,19 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
|
||||
break;
|
||||
default:
|
||||
/* success, or error we can't handle */
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
if (dp->dl_retries--) {
|
||||
rpc_delay(task, 2*HZ);
|
||||
task->tk_status = 0;
|
||||
rpc_restart_call(task);
|
||||
return;
|
||||
} else {
|
||||
atomic_set(&clp->cl_cb_conn.cb_set, 0);
|
||||
warn_no_callback_path(clp, task->tk_status);
|
||||
}
|
||||
done:
|
||||
kfree(task->tk_msg.rpc_argp);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_recall_release(void *calldata)
|
||||
@@ -542,6 +713,7 @@ static void nfsd4_cb_recall_release(void *calldata)
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfsd4_cb_recall_ops = {
|
||||
.rpc_call_prepare = nfsd4_cb_prepare,
|
||||
.rpc_call_done = nfsd4_cb_recall_done,
|
||||
.rpc_release = nfsd4_cb_recall_release,
|
||||
};
|
||||
@@ -554,17 +726,24 @@ nfsd4_cb_recall(struct nfs4_delegation *dp)
|
||||
{
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
|
||||
struct nfs4_rpc_args *args;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
|
||||
.rpc_argp = dp,
|
||||
.rpc_cred = clp->cl_cb_conn.cb_cred
|
||||
.rpc_cred = callback_cred
|
||||
};
|
||||
int status;
|
||||
int status = -ENOMEM;
|
||||
|
||||
args = kzalloc(sizeof(*args), GFP_KERNEL);
|
||||
if (!args)
|
||||
goto out;
|
||||
args->args_op = dp;
|
||||
msg.rpc_argp = args;
|
||||
dp->dl_retries = 1;
|
||||
status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
|
||||
&nfsd4_cb_recall_ops, dp);
|
||||
out:
|
||||
if (status) {
|
||||
kfree(args);
|
||||
put_nfs4_client(clp);
|
||||
nfs4_put_delegation(dp);
|
||||
}
|
||||
|
@@ -68,7 +68,6 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
u32 *bmval, u32 *writable)
|
||||
{
|
||||
struct dentry *dentry = cstate->current_fh.fh_dentry;
|
||||
struct svc_export *exp = cstate->current_fh.fh_export;
|
||||
|
||||
/*
|
||||
* Check about attributes are supported by the NFSv4 server or not.
|
||||
@@ -80,17 +79,13 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
return nfserr_attrnotsupp;
|
||||
|
||||
/*
|
||||
* Check FATTR4_WORD0_ACL & FATTR4_WORD0_FS_LOCATIONS can be supported
|
||||
* Check FATTR4_WORD0_ACL can be supported
|
||||
* in current environment or not.
|
||||
*/
|
||||
if (bmval[0] & FATTR4_WORD0_ACL) {
|
||||
if (!IS_POSIXACL(dentry->d_inode))
|
||||
return nfserr_attrnotsupp;
|
||||
}
|
||||
if (bmval[0] & FATTR4_WORD0_FS_LOCATIONS) {
|
||||
if (exp->ex_fslocs.locations == NULL)
|
||||
return nfserr_attrnotsupp;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to spec, read-only attributes return ERR_INVAL.
|
||||
@@ -123,6 +118,35 @@ nfsd4_check_open_attributes(struct svc_rqst *rqstp,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
is_create_with_attrs(struct nfsd4_open *open)
|
||||
{
|
||||
return open->op_create == NFS4_OPEN_CREATE
|
||||
&& (open->op_createmode == NFS4_CREATE_UNCHECKED
|
||||
|| open->op_createmode == NFS4_CREATE_GUARDED
|
||||
|| open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1);
|
||||
}
|
||||
|
||||
/*
|
||||
* if error occurs when setting the acl, just clear the acl bit
|
||||
* in the returned attr bitmap.
|
||||
*/
|
||||
static void
|
||||
do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct nfs4_acl *acl, u32 *bmval)
|
||||
{
|
||||
__be32 status;
|
||||
|
||||
status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
|
||||
if (status)
|
||||
/*
|
||||
* We should probably fail the whole open at this point,
|
||||
* but we've already created the file, so it's too late;
|
||||
* So this seems the least of evils:
|
||||
*/
|
||||
bmval[0] &= ~FATTR4_WORD0_ACL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fh_dup2(struct svc_fh *dst, struct svc_fh *src)
|
||||
{
|
||||
@@ -206,6 +230,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
if (is_create_with_attrs(open) && open->op_acl != NULL)
|
||||
do_set_nfs4_acl(rqstp, &resfh, open->op_acl, open->op_bmval);
|
||||
|
||||
set_change_info(&open->op_cinfo, current_fh);
|
||||
fh_dup2(current_fh, &resfh);
|
||||
|
||||
@@ -536,12 +563,17 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
status = nfserr_badtype;
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
fh_unlock(&cstate->current_fh);
|
||||
set_change_info(&create->cr_cinfo, &cstate->current_fh);
|
||||
fh_dup2(&cstate->current_fh, &resfh);
|
||||
}
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
if (create->cr_acl != NULL)
|
||||
do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
|
||||
create->cr_bmval);
|
||||
|
||||
fh_unlock(&cstate->current_fh);
|
||||
set_change_info(&create->cr_cinfo, &cstate->current_fh);
|
||||
fh_dup2(&cstate->current_fh, &resfh);
|
||||
out:
|
||||
fh_put(&resfh);
|
||||
return status;
|
||||
}
|
||||
@@ -946,34 +978,6 @@ static struct nfsd4_operation nfsd4_ops[];
|
||||
|
||||
static const char *nfsd4_op_name(unsigned opnum);
|
||||
|
||||
/*
|
||||
* This is a replay of a compound for which no cache entry pages
|
||||
* were used. Encode the sequence operation, and if cachethis is FALSE
|
||||
* encode the uncache rep error on the next operation.
|
||||
*/
|
||||
static __be32
|
||||
nfsd4_enc_uncached_replay(struct nfsd4_compoundargs *args,
|
||||
struct nfsd4_compoundres *resp)
|
||||
{
|
||||
struct nfsd4_op *op;
|
||||
|
||||
dprintk("--> %s resp->opcnt %d ce_cachethis %u \n", __func__,
|
||||
resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis);
|
||||
|
||||
/* Encode the replayed sequence operation */
|
||||
BUG_ON(resp->opcnt != 1);
|
||||
op = &args->ops[resp->opcnt - 1];
|
||||
nfsd4_encode_operation(resp, op);
|
||||
|
||||
/*return nfserr_retry_uncached_rep in next operation. */
|
||||
if (resp->cstate.slot->sl_cache_entry.ce_cachethis == 0) {
|
||||
op = &args->ops[resp->opcnt++];
|
||||
op->status = nfserr_retry_uncached_rep;
|
||||
nfsd4_encode_operation(resp, op);
|
||||
}
|
||||
return op->status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enforce NFSv4.1 COMPOUND ordering rules.
|
||||
*
|
||||
@@ -1083,13 +1087,10 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
|
||||
BUG_ON(op->status == nfs_ok);
|
||||
|
||||
encode_op:
|
||||
/* Only from SEQUENCE or CREATE_SESSION */
|
||||
/* Only from SEQUENCE */
|
||||
if (resp->cstate.status == nfserr_replay_cache) {
|
||||
dprintk("%s NFS4.1 replay from cache\n", __func__);
|
||||
if (nfsd4_not_cached(resp))
|
||||
status = nfsd4_enc_uncached_replay(args, resp);
|
||||
else
|
||||
status = op->status;
|
||||
status = op->status;
|
||||
goto out;
|
||||
}
|
||||
if (op->status == nfserr_replay_me) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1599,7 +1599,8 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
|
||||
static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
|
||||
{
|
||||
struct svc_fh tmp_fh;
|
||||
char *path, *rootpath;
|
||||
char *path = NULL, *rootpath;
|
||||
size_t rootlen;
|
||||
|
||||
fh_init(&tmp_fh, NFS4_FHSIZE);
|
||||
*stat = exp_pseudoroot(rqstp, &tmp_fh);
|
||||
@@ -1609,14 +1610,18 @@ static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *
|
||||
|
||||
path = exp->ex_pathname;
|
||||
|
||||
if (strncmp(path, rootpath, strlen(rootpath))) {
|
||||
rootlen = strlen(rootpath);
|
||||
if (strncmp(path, rootpath, rootlen)) {
|
||||
dprintk("nfsd: fs_locations failed;"
|
||||
"%s is not contained in %s\n", path, rootpath);
|
||||
*stat = nfserr_notsupp;
|
||||
return NULL;
|
||||
path = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return path + strlen(rootpath);
|
||||
path += rootlen;
|
||||
out:
|
||||
fh_put(&tmp_fh);
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1793,11 +1798,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
||||
goto out_nfserr;
|
||||
}
|
||||
}
|
||||
if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
|
||||
if (exp->ex_fslocs.locations == NULL) {
|
||||
bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS;
|
||||
}
|
||||
}
|
||||
if ((buflen -= 16) < 0)
|
||||
goto out_resource;
|
||||
|
||||
@@ -1825,8 +1825,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
||||
goto out_resource;
|
||||
if (!aclsupport)
|
||||
word0 &= ~FATTR4_WORD0_ACL;
|
||||
if (!exp->ex_fslocs.locations)
|
||||
word0 &= ~FATTR4_WORD0_FS_LOCATIONS;
|
||||
if (!word2) {
|
||||
WRITE32(2);
|
||||
WRITE32(word0);
|
||||
@@ -3064,6 +3062,7 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
|
||||
WRITE32(0);
|
||||
|
||||
ADJUST_ARGS();
|
||||
resp->cstate.datap = p; /* DRC cache data pointer */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3166,7 +3165,7 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
|
||||
return status;
|
||||
|
||||
session = resp->cstate.session;
|
||||
if (session == NULL || slot->sl_cache_entry.ce_cachethis == 0)
|
||||
if (session == NULL || slot->sl_cachethis == 0)
|
||||
return status;
|
||||
|
||||
if (resp->opcnt >= args->opcnt)
|
||||
@@ -3291,6 +3290,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
|
||||
/*
|
||||
* All that remains is to write the tag and operation count...
|
||||
*/
|
||||
struct nfsd4_compound_state *cs = &resp->cstate;
|
||||
struct kvec *iov;
|
||||
p = resp->tagp;
|
||||
*p++ = htonl(resp->taglen);
|
||||
@@ -3304,17 +3304,11 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
|
||||
iov = &rqstp->rq_res.head[0];
|
||||
iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
|
||||
BUG_ON(iov->iov_len > PAGE_SIZE);
|
||||
if (nfsd4_has_session(&resp->cstate)) {
|
||||
if (resp->cstate.status == nfserr_replay_cache &&
|
||||
!nfsd4_not_cached(resp)) {
|
||||
iov->iov_len = resp->cstate.iovlen;
|
||||
} else {
|
||||
nfsd4_store_cache_entry(resp);
|
||||
dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
|
||||
resp->cstate.slot->sl_inuse = 0;
|
||||
}
|
||||
if (resp->cstate.session)
|
||||
nfsd4_put_session(resp->cstate.session);
|
||||
if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) {
|
||||
nfsd4_store_cache_entry(resp);
|
||||
dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
|
||||
resp->cstate.slot->sl_inuse = false;
|
||||
nfsd4_put_session(resp->cstate.session);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@@ -174,12 +174,13 @@ static const struct file_operations exports_operations = {
|
||||
};
|
||||
|
||||
extern int nfsd_pool_stats_open(struct inode *inode, struct file *file);
|
||||
extern int nfsd_pool_stats_release(struct inode *inode, struct file *file);
|
||||
|
||||
static struct file_operations pool_stats_operations = {
|
||||
.open = nfsd_pool_stats_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
.release = nfsd_pool_stats_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
@@ -776,10 +777,7 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
|
||||
size -= len;
|
||||
mesg += len;
|
||||
}
|
||||
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return (mesg-buf);
|
||||
|
||||
rv = mesg - buf;
|
||||
out_free:
|
||||
kfree(nthreads);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
|
160
fs/nfsd/nfsfh.c
160
fs/nfsd/nfsfh.c
@@ -397,44 +397,51 @@ static inline void _fh_update_old(struct dentry *dentry,
|
||||
fh->ofh_dirino = 0;
|
||||
}
|
||||
|
||||
__be32
|
||||
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
struct svc_fh *ref_fh)
|
||||
static bool is_root_export(struct svc_export *exp)
|
||||
{
|
||||
/* ref_fh is a reference file handle.
|
||||
* if it is non-null and for the same filesystem, then we should compose
|
||||
* a filehandle which is of the same version, where possible.
|
||||
* Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
|
||||
* Then create a 32byte filehandle using nfs_fhbase_old
|
||||
*
|
||||
*/
|
||||
return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root;
|
||||
}
|
||||
|
||||
static struct super_block *exp_sb(struct svc_export *exp)
|
||||
{
|
||||
return exp->ex_path.dentry->d_inode->i_sb;
|
||||
}
|
||||
|
||||
static bool fsid_type_ok_for_exp(u8 fsid_type, struct svc_export *exp)
|
||||
{
|
||||
switch (fsid_type) {
|
||||
case FSID_DEV:
|
||||
if (!old_valid_dev(exp_sb(exp)->s_dev))
|
||||
return 0;
|
||||
/* FALL THROUGH */
|
||||
case FSID_MAJOR_MINOR:
|
||||
case FSID_ENCODE_DEV:
|
||||
return exp_sb(exp)->s_type->fs_flags & FS_REQUIRES_DEV;
|
||||
case FSID_NUM:
|
||||
return exp->ex_flags & NFSEXP_FSID;
|
||||
case FSID_UUID8:
|
||||
case FSID_UUID16:
|
||||
if (!is_root_export(exp))
|
||||
return 0;
|
||||
/* fall through */
|
||||
case FSID_UUID4_INUM:
|
||||
case FSID_UUID16_INUM:
|
||||
return exp->ex_uuid != NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void set_version_and_fsid_type(struct svc_fh *fhp, struct svc_export *exp, struct svc_fh *ref_fh)
|
||||
{
|
||||
u8 version;
|
||||
u8 fsid_type = 0;
|
||||
struct inode * inode = dentry->d_inode;
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
__u32 *datap;
|
||||
dev_t ex_dev = exp->ex_path.dentry->d_inode->i_sb->s_dev;
|
||||
int root_export = (exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root);
|
||||
|
||||
dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
|
||||
MAJOR(ex_dev), MINOR(ex_dev),
|
||||
(long) exp->ex_path.dentry->d_inode->i_ino,
|
||||
parent->d_name.name, dentry->d_name.name,
|
||||
(inode ? inode->i_ino : 0));
|
||||
|
||||
/* Choose filehandle version and fsid type based on
|
||||
* the reference filehandle (if it is in the same export)
|
||||
* or the export options.
|
||||
*/
|
||||
retry:
|
||||
u8 fsid_type;
|
||||
retry:
|
||||
version = 1;
|
||||
if (ref_fh && ref_fh->fh_export == exp) {
|
||||
version = ref_fh->fh_handle.fh_version;
|
||||
fsid_type = ref_fh->fh_handle.fh_fsid_type;
|
||||
|
||||
if (ref_fh == fhp)
|
||||
fh_put(ref_fh);
|
||||
ref_fh = NULL;
|
||||
|
||||
switch (version) {
|
||||
@@ -447,58 +454,66 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* Need to check that this type works for this
|
||||
* export point. As the fsid -> filesystem mapping
|
||||
* was guided by user-space, there is no guarantee
|
||||
* that the filesystem actually supports that fsid
|
||||
* type. If it doesn't we loop around again without
|
||||
* ref_fh set.
|
||||
/*
|
||||
* As the fsid -> filesystem mapping was guided by
|
||||
* user-space, there is no guarantee that the filesystem
|
||||
* actually supports that fsid type. If it doesn't we
|
||||
* loop around again without ref_fh set.
|
||||
*/
|
||||
switch(fsid_type) {
|
||||
case FSID_DEV:
|
||||
if (!old_valid_dev(ex_dev))
|
||||
goto retry;
|
||||
/* FALL THROUGH */
|
||||
case FSID_MAJOR_MINOR:
|
||||
case FSID_ENCODE_DEV:
|
||||
if (!(exp->ex_path.dentry->d_inode->i_sb->s_type->fs_flags
|
||||
& FS_REQUIRES_DEV))
|
||||
goto retry;
|
||||
break;
|
||||
case FSID_NUM:
|
||||
if (! (exp->ex_flags & NFSEXP_FSID))
|
||||
goto retry;
|
||||
break;
|
||||
case FSID_UUID8:
|
||||
case FSID_UUID16:
|
||||
if (!root_export)
|
||||
goto retry;
|
||||
/* fall through */
|
||||
case FSID_UUID4_INUM:
|
||||
case FSID_UUID16_INUM:
|
||||
if (exp->ex_uuid == NULL)
|
||||
goto retry;
|
||||
break;
|
||||
}
|
||||
if (!fsid_type_ok_for_exp(fsid_type, exp))
|
||||
goto retry;
|
||||
} else if (exp->ex_flags & NFSEXP_FSID) {
|
||||
fsid_type = FSID_NUM;
|
||||
} else if (exp->ex_uuid) {
|
||||
if (fhp->fh_maxsize >= 64) {
|
||||
if (root_export)
|
||||
if (is_root_export(exp))
|
||||
fsid_type = FSID_UUID16;
|
||||
else
|
||||
fsid_type = FSID_UUID16_INUM;
|
||||
} else {
|
||||
if (root_export)
|
||||
if (is_root_export(exp))
|
||||
fsid_type = FSID_UUID8;
|
||||
else
|
||||
fsid_type = FSID_UUID4_INUM;
|
||||
}
|
||||
} else if (!old_valid_dev(ex_dev))
|
||||
} else if (!old_valid_dev(exp_sb(exp)->s_dev))
|
||||
/* for newer device numbers, we must use a newer fsid format */
|
||||
fsid_type = FSID_ENCODE_DEV;
|
||||
else
|
||||
fsid_type = FSID_DEV;
|
||||
fhp->fh_handle.fh_version = version;
|
||||
if (version)
|
||||
fhp->fh_handle.fh_fsid_type = fsid_type;
|
||||
}
|
||||
|
||||
__be32
|
||||
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
struct svc_fh *ref_fh)
|
||||
{
|
||||
/* ref_fh is a reference file handle.
|
||||
* if it is non-null and for the same filesystem, then we should compose
|
||||
* a filehandle which is of the same version, where possible.
|
||||
* Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
|
||||
* Then create a 32byte filehandle using nfs_fhbase_old
|
||||
*
|
||||
*/
|
||||
|
||||
struct inode * inode = dentry->d_inode;
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
__u32 *datap;
|
||||
dev_t ex_dev = exp_sb(exp)->s_dev;
|
||||
|
||||
dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
|
||||
MAJOR(ex_dev), MINOR(ex_dev),
|
||||
(long) exp->ex_path.dentry->d_inode->i_ino,
|
||||
parent->d_name.name, dentry->d_name.name,
|
||||
(inode ? inode->i_ino : 0));
|
||||
|
||||
/* Choose filehandle version and fsid type based on
|
||||
* the reference filehandle (if it is in the same export)
|
||||
* or the export options.
|
||||
*/
|
||||
set_version_and_fsid_type(fhp, exp, ref_fh);
|
||||
|
||||
if (ref_fh == fhp)
|
||||
fh_put(ref_fh);
|
||||
@@ -516,7 +531,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
fhp->fh_export = exp;
|
||||
cache_get(&exp->h);
|
||||
|
||||
if (version == 0xca) {
|
||||
if (fhp->fh_handle.fh_version == 0xca) {
|
||||
/* old style filehandle please */
|
||||
memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
|
||||
fhp->fh_handle.fh_size = NFS_FHSIZE;
|
||||
@@ -530,22 +545,22 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
_fh_update_old(dentry, exp, &fhp->fh_handle);
|
||||
} else {
|
||||
int len;
|
||||
fhp->fh_handle.fh_version = 1;
|
||||
fhp->fh_handle.fh_auth_type = 0;
|
||||
datap = fhp->fh_handle.fh_auth+0;
|
||||
fhp->fh_handle.fh_fsid_type = fsid_type;
|
||||
mk_fsid(fsid_type, datap, ex_dev,
|
||||
mk_fsid(fhp->fh_handle.fh_fsid_type, datap, ex_dev,
|
||||
exp->ex_path.dentry->d_inode->i_ino,
|
||||
exp->ex_fsid, exp->ex_uuid);
|
||||
|
||||
len = key_len(fsid_type);
|
||||
len = key_len(fhp->fh_handle.fh_fsid_type);
|
||||
datap += len/4;
|
||||
fhp->fh_handle.fh_size = 4 + len;
|
||||
|
||||
if (inode)
|
||||
_fh_update(fhp, exp, dentry);
|
||||
if (fhp->fh_handle.fh_fileid_type == 255)
|
||||
if (fhp->fh_handle.fh_fileid_type == 255) {
|
||||
fh_put(fhp);
|
||||
return nfserr_opnotsupp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -639,8 +654,7 @@ enum fsid_source fsid_source(struct svc_fh *fhp)
|
||||
case FSID_DEV:
|
||||
case FSID_ENCODE_DEV:
|
||||
case FSID_MAJOR_MINOR:
|
||||
if (fhp->fh_export->ex_path.dentry->d_inode->i_sb->s_type->fs_flags
|
||||
& FS_REQUIRES_DEV)
|
||||
if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV)
|
||||
return FSIDSOURCE_DEV;
|
||||
break;
|
||||
case FSID_NUM:
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include <linux/nfsd/syscall.h>
|
||||
#include <linux/lockd/bind.h>
|
||||
#include <linux/nfsacl.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
||||
|
||||
@@ -66,6 +67,16 @@ struct timeval nfssvc_boot;
|
||||
DEFINE_MUTEX(nfsd_mutex);
|
||||
struct svc_serv *nfsd_serv;
|
||||
|
||||
/*
|
||||
* nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
|
||||
* nfsd_drc_max_pages limits the total amount of memory available for
|
||||
* version 4.1 DRC caches.
|
||||
* nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
|
||||
*/
|
||||
spinlock_t nfsd_drc_lock;
|
||||
unsigned int nfsd_drc_max_mem;
|
||||
unsigned int nfsd_drc_mem_used;
|
||||
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
static struct svc_stat nfsd_acl_svcstats;
|
||||
static struct svc_version * nfsd_acl_version[] = {
|
||||
@@ -235,13 +246,12 @@ void nfsd_reset_versions(void)
|
||||
*/
|
||||
static void set_max_drc(void)
|
||||
{
|
||||
/* The percent of nr_free_buffer_pages used by the V4.1 server DRC */
|
||||
#define NFSD_DRC_SIZE_SHIFT 7
|
||||
nfsd_serv->sv_drc_max_pages = nr_free_buffer_pages()
|
||||
>> NFSD_DRC_SIZE_SHIFT;
|
||||
nfsd_serv->sv_drc_pages_used = 0;
|
||||
dprintk("%s svc_drc_max_pages %u\n", __func__,
|
||||
nfsd_serv->sv_drc_max_pages);
|
||||
#define NFSD_DRC_SIZE_SHIFT 10
|
||||
nfsd_drc_max_mem = (nr_free_buffer_pages()
|
||||
>> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
|
||||
nfsd_drc_mem_used = 0;
|
||||
spin_lock_init(&nfsd_drc_lock);
|
||||
dprintk("%s nfsd_drc_max_mem %u \n", __func__, nfsd_drc_max_mem);
|
||||
}
|
||||
|
||||
int nfsd_create_serv(void)
|
||||
@@ -401,7 +411,9 @@ nfsd_svc(unsigned short port, int nrservs)
|
||||
error = nfsd_racache_init(2*nrservs);
|
||||
if (error<0)
|
||||
goto out;
|
||||
nfs4_state_start();
|
||||
error = nfs4_state_start();
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
nfsd_reset_versions();
|
||||
|
||||
@@ -569,10 +581,6 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
+ rqstp->rq_res.head[0].iov_len;
|
||||
rqstp->rq_res.head[0].iov_len += sizeof(__be32);
|
||||
|
||||
/* NFSv4.1 DRC requires statp */
|
||||
if (rqstp->rq_vers == 4)
|
||||
nfsd4_set_statp(rqstp, statp);
|
||||
|
||||
/* Now call the procedure handler, and encode NFS status. */
|
||||
nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
|
||||
nfserr = map_new_errors(rqstp->rq_vers, nfserr);
|
||||
@@ -607,7 +615,25 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
|
||||
int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (nfsd_serv == NULL)
|
||||
int ret;
|
||||
mutex_lock(&nfsd_mutex);
|
||||
if (nfsd_serv == NULL) {
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return -ENODEV;
|
||||
return svc_pool_stats_open(nfsd_serv, file);
|
||||
}
|
||||
/* bump up the psudo refcount while traversing */
|
||||
svc_get(nfsd_serv);
|
||||
ret = svc_pool_stats_open(nfsd_serv, file);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nfsd_pool_stats_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = seq_release(inode, file);
|
||||
mutex_lock(&nfsd_mutex);
|
||||
/* this function really, really should have been called svc_put() */
|
||||
svc_destroy(nfsd_serv);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@@ -89,6 +89,12 @@ struct raparm_hbucket {
|
||||
#define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1)
|
||||
static struct raparm_hbucket raparm_hash[RAPARM_HASH_SIZE];
|
||||
|
||||
static inline int
|
||||
nfsd_v4client(struct svc_rqst *rq)
|
||||
{
|
||||
return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from nfsd_lookup and encode_dirent. Check if we have crossed
|
||||
* a mount point.
|
||||
@@ -115,7 +121,8 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
|
||||
path_put(&path);
|
||||
goto out;
|
||||
}
|
||||
if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
|
||||
if (nfsd_v4client(rqstp) ||
|
||||
(exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
|
||||
/* successfully crossed mount point */
|
||||
/*
|
||||
* This is subtle: path.dentry is *not* on path.mnt
|
||||
|
Reference in New Issue
Block a user