static int nfs_callback_authenticate(struct svc_rqst *rqstp) { struct nfs_client *clp; RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); int ret = SVC_OK; /* Don't talk to strangers */ clp = nfs_find_client(svc_addr(rqstp), 4); if (clp == NULL) return SVC_DROP; dprintk("%s: %s NFSv4 callback!\n", __func__, svc_print_addr(rqstp, buf, sizeof(buf))); switch (rqstp->rq_authop->flavour) { case RPC_AUTH_NULL: if (rqstp->rq_proc != CB_NULL) ret = SVC_DENIED; break; case RPC_AUTH_UNIX: break; case RPC_AUTH_GSS: ret = check_gss_callback_principal(clp, rqstp); break; default: ret = SVC_DENIED; } nfs_put_client(clp); return ret; }
/* Reduce the fore channel's max_slots to the target value */ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy) { struct nfs_client *clp; struct nfs4_slot_table *fc_tbl; __be32 status; status = htonl(NFS4ERR_OP_NOT_IN_SESSION); clp = nfs_find_client(args->crsa_addr, 4); if (clp == NULL) goto out; dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR), args->crsa_target_max_slots); fc_tbl = &clp->cl_session->fc_slot_table; status = htonl(NFS4ERR_BAD_HIGH_SLOT); if (args->crsa_target_max_slots > fc_tbl->max_slots || args->crsa_target_max_slots < 1) goto out_putclient; status = htonl(NFS4_OK); if (args->crsa_target_max_slots == fc_tbl->max_slots) goto out_putclient; fc_tbl->target_max_slots = args->crsa_target_max_slots; nfs41_handle_recall_slot(clp); out_putclient: nfs_put_client(clp); /* balance nfs_find_client */ out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; }
/* * Returns a pointer to a held 'struct nfs_client' that matches the server's * address, major version number, and session ID. It is the caller's * responsibility to release the returned reference. * * Returns NULL if there are no connections with sessions, or if no session * matches the one of interest. */ static struct nfs_client *find_client_with_session( const struct sockaddr *addr, u32 nfsversion, struct nfs4_sessionid *sessionid) { struct nfs_client *clp; clp = nfs_find_client(addr, 4); if (clp == NULL) return NULL; do { struct nfs_client *prev = clp; if (clp->cl_session != NULL) { if (memcmp(clp->cl_session->sess_id.data, sessionid->data, NFS4_MAX_SESSIONID_LEN) == 0) { /* Returns a held reference to clp */ return clp; } } clp = nfs_find_client_next(prev); nfs_put_client(prev); } while (clp != NULL); return NULL; }
static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp) { struct cb_compound_hdr_arg hdr_arg = { 0 }; struct cb_compound_hdr_res hdr_res = { NULL }; struct xdr_stream xdr_in, xdr_out; __be32 *p, status; struct cb_process_state cps = { .drc_status = 0, .clp = NULL, .slotid = NFS4_NO_SLOT, .net = rqstp->rq_xprt->xpt_net, }; unsigned int nops = 0; dprintk("%s: start\n", __func__); xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len); xdr_init_encode(&xdr_out, &rqstp->rq_res, p); status = decode_compound_hdr_arg(&xdr_in, &hdr_arg); if (status == __constant_htonl(NFS4ERR_RESOURCE)) return rpc_garbage_args; if (hdr_arg.minorversion == 0) { cps.clp = nfs4_find_client_ident(rqstp->rq_xprt->xpt_net, hdr_arg.cb_ident); if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) return rpc_drop_reply; } hdr_res.taglen = hdr_arg.taglen; hdr_res.tag = hdr_arg.tag; if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) return rpc_system_err; while (status == 0 && nops != hdr_arg.nops) { status = process_op(hdr_arg.minorversion, nops, rqstp, &xdr_in, argp, &xdr_out, resp, &cps); nops++; } if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) { status = htonl(NFS4ERR_RESOURCE); nops--; } *hdr_res.status = status; *hdr_res.nops = htonl(nops); nfs4_cb_free_slot(&cps); nfs_put_client(cps.clp); dprintk("%s: done, status = %u\n", __func__, ntohl(status)); return rpc_success; } static struct callback_op callback_ops[] = { [0] = { .res_maxsize = CB_OP_HDR_RES_MAXSZ, },
__be32 nfs4_callback_sequence(struct cb_sequenceargs *args, struct cb_sequenceres *res) { struct nfs_client *clp; int i; __be32 status; status = htonl(NFS4ERR_BADSESSION); clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); if (clp == NULL) goto out; status = validate_seqid(&clp->cl_session->bc_slot_table, args); if (status) goto out_putclient; /* * Check for pending referring calls. If a match is found, a * related callback was received before the response to the original * call. */ if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) { status = htonl(NFS4ERR_DELAY); goto out_putclient; } memcpy(&res->csr_sessionid, &args->csa_sessionid, sizeof(res->csr_sessionid)); res->csr_sequenceid = args->csa_sequenceid; res->csr_slotid = args->csa_slotid; res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; out_putclient: nfs_put_client(clp); out: for (i = 0; i < args->csa_nrclists; i++) kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists); if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) res->csr_status = 0; else res->csr_status = status; dprintk("%s: exit with status = %d res->csr_status %d\n", __func__, ntohl(status), ntohl(res->csr_status)); return status; }
__be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res) { struct nfs_client *clp; struct nfs_delegation *delegation; struct nfs_inode *nfsi; struct inode *inode; res->bitmap[0] = res->bitmap[1] = 0; res->status = htonl(NFS4ERR_BADHANDLE); clp = nfs_find_client(args->addr, 4); if (clp == NULL) goto out; dprintk("NFS: GETATTR callback request from %s\n", rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); inode = nfs_delegation_find_inode(clp, &args->fh); if (inode == NULL) goto out_putclient; nfsi = NFS_I(inode); rcu_read_lock(); delegation = rcu_dereference(nfsi->delegation); if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0) goto out_iput; res->size = i_size_read(inode); res->change_attr = delegation->change_attr; if (nfsi->npages != 0) res->change_attr++; res->ctime = inode->i_ctime; res->mtime = inode->i_mtime; res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) & args->bitmap[0]; res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) & args->bitmap[1]; res->status = 0; out_iput: rcu_read_unlock(); iput(inode); out_putclient: nfs_put_client(clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status)); return res->status; }
__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) { struct nfs_client *clp; struct inode *inode; __be32 res; res = htonl(NFS4ERR_BADHANDLE); clp = nfs_find_client(args->addr, 4); if (clp == NULL) goto out; dprintk("NFS: RECALL callback request from %s\n", rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); do { struct nfs_client *prev = clp; inode = nfs_delegation_find_inode(clp, &args->fh); if (inode != NULL) { /* Set up a helper thread to actually return the delegation */ switch (nfs_async_inode_return_delegation(inode, &args->stateid, nfs_validate_delegation_stateid(clp))) { case 0: res = 0; break; case -ENOENT: if (res != 0) res = htonl(NFS4ERR_BAD_STATEID); break; default: res = htonl(NFS4ERR_RESOURCE); } iput(inode); } clp = nfs_find_client_next(prev); nfs_put_client(prev); } while (clp != NULL); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); return res; }
int migrate_nfs_update_server(struct nfs_server *server, const char *hostname, struct sockaddr *sap, size_t salen, struct net *net) { struct nfs_client *clp = server->nfs_client; struct rpc_clnt *clnt = server->client; struct xprt_create xargs = { .ident = clp->cl_proto, .net = net, .dstaddr = sap, .addrlen = salen, .servername = hostname, }; char buf[INET6_ADDRSTRLEN + 1]; struct sockaddr_storage address; struct sockaddr *localaddr = (struct sockaddr *)&address; int error; dfprintk(MOUNT, "zql: --> %s: move FSID %llx:%llx to \"%s\")\n", __func__, (unsigned long long)server->fsid.major, (unsigned long long)server->fsid.minor, hostname); error = rpc_switch_client_transport(clnt, &xargs, clnt->cl_timeout); if (error != 0) { dfprintk(MOUNT, "zql: <-- %s(): rpc_switch_client_transport returned %d\n", __func__, error); goto out; } error = rpc_localaddr(clnt, localaddr, sizeof(address)); if (error != 0) { dfprintk(MOUNT, "zql: <-- %s(): rpc_localaddr returned %d\n", __func__, error); goto out; } error = -EAFNOSUPPORT; if (rpc_ntop(localaddr, buf, sizeof(buf)) == 0) { dfprintk(MOUNT, "zql: <-- %s(): rpc_ntop returned %d\n", __func__, error); goto out; } nfs_server_remove_lists(server); error = migrate_nfs_set_client(server, hostname, sap, salen, buf, clp->cl_rpcclient->cl_auth->au_flavor, clp->cl_proto, clnt->cl_timeout, clp->cl_minorversion, net); nfs_put_client(clp); if (error != 0) { nfs_server_insert_lists(server); dfprintk(MOUNT, "zql: <-- %s(): nfs4_set_client returned %d\n", __func__, error); goto out; } if (server->nfs_client->cl_hostname == NULL) server->nfs_client->cl_hostname = kstrdup(hostname, GFP_KERNEL); nfs_server_insert_lists(server); error = migrate_nfs_probe_destination(server); if (error < 0) { dfprintk(MOUNT, "zql: error in migrate_nfs_probe_destination\n"); goto out; } dfprintk(MOUNT, "zql: <-- %s() succeeded\n", __func__); out: dfprintk(MOUNT, "zql: error in %s()\n", __func__); return error; } static void zql_update_server(struct nfs_server *server) { /* * we do not have to free server, * instead, we only update struct nfs_server. */ //nfs_free_server(server); /* free old nfs_client, though we can leave it there and use it when switch back */ //server->nfs_client->rpc_ops->free_client(server->nfs_client); //struct nfs_server parent_server; //struct nfs_client *parent_client; //struct nfs_subversion *nfs_mod; struct nfs_parsed_mount_data *parsed; //char raw_data[] = "addr=162.105.146.169,vers=3,proto=tcp,mountvers=3,mountproto=udp,mountport=20048"; /* keep old server parameter, copy original server to a temporary parameter */ //parent_server = *server; //parent_client = parent_server.nfs_client; /* generate nfs_parsed_mount_data */ parsed = nfs_alloc_parsed_mount_data(); if (parsed == NULL) dfprintk(MOUNT, "zql: nfs_alloc_parsed_mount_data error\n"); /* init nfs_parsed_mount_data */ if (nfs_zql_control == 168) migrate_nfs_validate_text_mount_data168(/*raw_data, */parsed/*, dev_name*/); else if (nfs_zql_control == 169) migrate_nfs_validate_text_mount_data169(parsed); else dfprintk(MOUNT, "zql: nfs_zql_control error\n"); migrate_nfs_update_server(server, parsed->nfs_server.hostname, (struct sockaddr *)&parsed->nfs_server.address, parsed->nfs_server.addrlen, parsed->net); /*nfs_mod = get_nfs_version(parsed->version); if (IS_ERR(nfs_mod)) dfprintk(MOUNT, "zql: get_nfs_version error\n");*/ /* init server->nfs_client */ //migrate_nfs_set_client(server, parsed, nfs_mod, parent_server.client->cl_timeout); //nfs_init_server_rpcclient(server, parent_server.client->cl_timeout, parsed->selected_flavor); //put_nfs_version(nfs_mod); nfs_free_parsed_mount_data(parsed); return; } static void zql_control_test(struct nfs_server *server) { if (nfs_zql_control == 5 || nfs_zql_control == 168 || nfs_zql_control == 169) { dfprintk(MOUNT, "zql: control start ... %d\n", nfs_zql_control); while (nfs_zql_control == 5) { msleep_interruptible(500); } if (nfs_zql_control == 168 || nfs_zql_control == 169) { spin_lock(&zql_switch_status); if (switch_status == 0) { switch_status = 1; spin_unlock(&zql_switch_status); dfprintk(MOUNT, "zql: switch succeed\n"); dfprintk(MOUNT, "zql: start update server\n"); zql_update_server(server); switch_status = 0; nfs_zql_control = 10; dfprintk(MOUNT, "zql: update server done\n"); } else { spin_unlock(&zql_switch_status); dfprintk(MOUNT, "zql: switch_status check\n"); while (switch_status == 1) { msleep_interruptible(100); } dfprintk(MOUNT, "zql: switch_status done\n"); } } else if (nfs_zql_control == 4) dfprintk(MOUNT, "zql: switch failed\n"); else dfprintk(MOUNT, "zql: unknown failure\n"); } } /* zql code end */ static const struct inode_operations nfs3_dir_inode_operations = { .create = nfs_create, .lookup = nfs_lookup, .link = nfs_link, .unlink = nfs_unlink, .symlink = nfs_symlink, .mkdir = nfs_mkdir, .rmdir = nfs_rmdir, .mknod = nfs_mknod, .rename = nfs_rename, .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, #ifdef CONFIG_NFS_V3_ACL .listxattr = nfs3_listxattr, .getxattr = generic_getxattr, .setxattr = generic_setxattr, .removexattr = generic_removexattr, .get_acl = nfs3_get_acl, .set_acl = nfs3_set_acl, #endif }; static const struct inode_operations nfs3_file_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, #ifdef CONFIG_NFS_V3_ACL .listxattr = nfs3_listxattr, .getxattr = generic_getxattr, .setxattr = generic_setxattr, .removexattr = generic_removexattr, .get_acl = nfs3_get_acl, .set_acl = nfs3_set_acl, #endif }; const struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, .dir_inode_ops = &nfs3_dir_inode_operations, .file_inode_ops = &nfs3_file_inode_operations, .file_ops = &nfs_file_operations, .getroot = nfs3_proc_get_root, .submount = nfs_submount, .try_mount = nfs_try_mount, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, .lookup = nfs3_proc_lookup, .access = nfs3_proc_access, .readlink = nfs3_proc_readlink, .create = nfs3_proc_create, .remove = nfs3_proc_remove, .unlink_setup = nfs3_proc_unlink_setup, .unlink_rpc_prepare = nfs3_proc_unlink_rpc_prepare, .unlink_done = nfs3_proc_unlink_done, .rename_setup = nfs3_proc_rename_setup, .rename_rpc_prepare = nfs3_proc_rename_rpc_prepare, .rename_done = nfs3_proc_rename_done, .link = nfs3_proc_link, .symlink = nfs3_proc_symlink, .mkdir = nfs3_proc_mkdir, .rmdir = nfs3_proc_rmdir, .readdir = nfs3_proc_readdir, .mknod = nfs3_proc_mknod, .statfs = nfs3_proc_statfs, .fsinfo = nfs3_proc_fsinfo, .pathconf = nfs3_proc_pathconf, .decode_dirent = nfs3_decode_dirent, .pgio_rpc_prepare = nfs3_proc_pgio_rpc_prepare, .read_setup = nfs3_proc_read_setup, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, .commit_rpc_prepare = nfs3_proc_commit_rpc_prepare, .commit_done = nfs3_commit_done, .lock = nfs3_proc_lock, .clear_acl_cache = forget_all_cached_acls, .close_context = nfs_close_context, .have_delegation = nfs3_have_delegation, .return_delegation = nfs3_return_delegation, .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, .create_server = nfs3_create_server, .clone_server = nfs3_clone_server, };
/** * nfs4_init_client - Initialise an NFS4 client record * * @clp: nfs_client to initialise * @timeparms: timeout parameters for underlying RPC transport * @ip_addr: callback IP address in presentation format * @authflavor: authentication flavor for underlying RPC transport * * Returns pointer to an NFS client, or an ERR_PTR value. */ struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, const char *ip_addr, rpc_authflavor_t authflavour) { char buf[INET6_ADDRSTRLEN + 1]; struct nfs_client *old; int error; if (clp->cl_cons_state == NFS_CS_READY) { /* the client is initialised already */ dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); return clp; } /* Check NFS protocol revision and initialize RPC op vector */ clp->rpc_ops = &nfs_v4_clientops; if (clp->cl_minorversion != 0) __set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags); __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I); if (error == -EINVAL) error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); if (error < 0) goto error; /* If no clientaddr= option was specified, find a usable cb address */ if (ip_addr == NULL) { struct sockaddr_storage cb_addr; struct sockaddr *sap = (struct sockaddr *)&cb_addr; error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr)); if (error < 0) goto error; error = rpc_ntop(sap, buf, sizeof(buf)); if (error < 0) goto error; ip_addr = (const char *)buf; } strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); error = nfs_idmap_new(clp); if (error < 0) { dprintk("%s: failed to create idmapper. Error = %d\n", __func__, error); goto error; } __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); error = nfs4_init_client_minor_version(clp); if (error < 0) goto error; if (!nfs4_has_session(clp)) nfs_mark_client_ready(clp, NFS_CS_READY); error = nfs4_discover_server_trunking(clp, &old); if (error < 0) goto error; if (clp != old) clp->cl_preserve_clid = true; nfs_put_client(clp); return old; error: nfs_mark_client_ready(clp, error); nfs_put_client(clp); dprintk("<-- nfs4_init_client() = xerror %d\n", error); return ERR_PTR(error); }
/* * Decode, process and encode a COMPOUND */ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp) { struct cb_compound_hdr_arg hdr_arg = { 0 }; struct cb_compound_hdr_res hdr_res = { NULL }; struct xdr_stream xdr_in, xdr_out; __be32 *p, status; struct cb_process_state cps = { .drc_status = 0, .clp = NULL, .net = SVC_NET(rqstp), }; unsigned int nops = 0; dprintk("%s: start\n", __func__); xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len); xdr_init_encode(&xdr_out, &rqstp->rq_res, p); status = decode_compound_hdr_arg(&xdr_in, &hdr_arg); if (status == htonl(NFS4ERR_RESOURCE)) return rpc_garbage_args; if (hdr_arg.minorversion == 0) { cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident); if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) goto out_invalidcred; } cps.minorversion = hdr_arg.minorversion; hdr_res.taglen = hdr_arg.taglen; hdr_res.tag = hdr_arg.tag; if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) return rpc_system_err; while (status == 0 && nops != hdr_arg.nops) { status = process_op(nops, rqstp, &xdr_in, argp, &xdr_out, resp, &cps); nops++; } /* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return * resource error in cb_compound status without returning op */ if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) { status = htonl(NFS4ERR_RESOURCE); nops--; } *hdr_res.status = status; *hdr_res.nops = htonl(nops); nfs4_cb_free_slot(&cps); nfs_put_client(cps.clp); dprintk("%s: done, status = %u\n", __func__, ntohl(status)); return rpc_success; out_invalidcred: pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n"); return rpc_autherr_badcred; } /* * Define NFS4 callback COMPOUND ops. */ static struct callback_op callback_ops[] = { [0] = { .res_maxsize = CB_OP_HDR_RES_MAXSZ, },