PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/fs/nfs/callback.c

https://github.com/Xmister/i5700-leshak-kernel
C | 257 lines | 184 code | 31 blank | 42 comment | 37 complexity | 25381c8a0e5a0cdbeb9c0811d99f0925 MD5 | raw file
  1. /*
  2. * linux/fs/nfs/callback.c
  3. *
  4. * Copyright (C) 2004 Trond Myklebust
  5. *
  6. * NFSv4 callback handling
  7. */
  8. #include <linux/completion.h>
  9. #include <linux/ip.h>
  10. #include <linux/module.h>
  11. #include <linux/smp_lock.h>
  12. #include <linux/sunrpc/svc.h>
  13. #include <linux/sunrpc/svcsock.h>
  14. #include <linux/nfs_fs.h>
  15. #include <linux/mutex.h>
  16. #include <linux/freezer.h>
  17. #include <linux/kthread.h>
  18. #include <linux/sunrpc/svcauth_gss.h>
  19. #include <net/inet_sock.h>
  20. #include "nfs4_fs.h"
  21. #include "callback.h"
  22. #include "internal.h"
  23. #define NFSDBG_FACILITY NFSDBG_CALLBACK
  24. struct nfs_callback_data {
  25. unsigned int users;
  26. struct svc_rqst *rqst;
  27. struct task_struct *task;
  28. };
  29. static struct nfs_callback_data nfs_callback_info;
  30. static DEFINE_MUTEX(nfs_callback_mutex);
  31. static struct svc_program nfs4_callback_program;
  32. unsigned int nfs_callback_set_tcpport;
  33. unsigned short nfs_callback_tcpport;
  34. static const int nfs_set_port_min = 0;
  35. static const int nfs_set_port_max = 65535;
  36. /*
  37. * If the kernel has IPv6 support available, always listen for
  38. * both AF_INET and AF_INET6 requests.
  39. */
  40. #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
  41. static const sa_family_t nfs_callback_family = AF_INET6;
  42. #else
  43. static const sa_family_t nfs_callback_family = AF_INET;
  44. #endif
  45. static int param_set_port(const char *val, struct kernel_param *kp)
  46. {
  47. char *endp;
  48. int num = simple_strtol(val, &endp, 0);
  49. if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
  50. return -EINVAL;
  51. *((int *)kp->arg) = num;
  52. return 0;
  53. }
  54. module_param_call(callback_tcpport, param_set_port, param_get_int,
  55. &nfs_callback_set_tcpport, 0644);
  56. /*
  57. * This is the callback kernel thread.
  58. */
  59. static int
  60. nfs_callback_svc(void *vrqstp)
  61. {
  62. int err, preverr = 0;
  63. struct svc_rqst *rqstp = vrqstp;
  64. set_freezable();
  65. /*
  66. * FIXME: do we really need to run this under the BKL? If so, please
  67. * add a comment about what it's intended to protect.
  68. */
  69. lock_kernel();
  70. while (!kthread_should_stop()) {
  71. /*
  72. * Listen for a request on the socket
  73. */
  74. err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
  75. if (err == -EAGAIN || err == -EINTR) {
  76. preverr = err;
  77. continue;
  78. }
  79. if (err < 0) {
  80. if (err != preverr) {
  81. printk(KERN_WARNING "%s: unexpected error "
  82. "from svc_recv (%d)\n", __func__, err);
  83. preverr = err;
  84. }
  85. schedule_timeout_uninterruptible(HZ);
  86. continue;
  87. }
  88. preverr = err;
  89. svc_process(rqstp);
  90. }
  91. unlock_kernel();
  92. return 0;
  93. }
  94. /*
  95. * Bring up the callback thread if it is not already up.
  96. */
  97. int nfs_callback_up(void)
  98. {
  99. struct svc_serv *serv = NULL;
  100. int ret = 0;
  101. mutex_lock(&nfs_callback_mutex);
  102. if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
  103. goto out;
  104. serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
  105. nfs_callback_family, NULL);
  106. ret = -ENOMEM;
  107. if (!serv)
  108. goto out_err;
  109. ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
  110. SVC_SOCK_ANONYMOUS);
  111. if (ret <= 0)
  112. goto out_err;
  113. nfs_callback_tcpport = ret;
  114. dprintk("NFS: Callback listener port = %u (af %u)\n",
  115. nfs_callback_tcpport, nfs_callback_family);
  116. nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
  117. if (IS_ERR(nfs_callback_info.rqst)) {
  118. ret = PTR_ERR(nfs_callback_info.rqst);
  119. nfs_callback_info.rqst = NULL;
  120. goto out_err;
  121. }
  122. svc_sock_update_bufs(serv);
  123. nfs_callback_info.task = kthread_run(nfs_callback_svc,
  124. nfs_callback_info.rqst,
  125. "nfsv4-svc");
  126. if (IS_ERR(nfs_callback_info.task)) {
  127. ret = PTR_ERR(nfs_callback_info.task);
  128. svc_exit_thread(nfs_callback_info.rqst);
  129. nfs_callback_info.rqst = NULL;
  130. nfs_callback_info.task = NULL;
  131. goto out_err;
  132. }
  133. out:
  134. /*
  135. * svc_create creates the svc_serv with sv_nrthreads == 1, and then
  136. * svc_prepare_thread increments that. So we need to call svc_destroy
  137. * on both success and failure so that the refcount is 1 when the
  138. * thread exits.
  139. */
  140. if (serv)
  141. svc_destroy(serv);
  142. mutex_unlock(&nfs_callback_mutex);
  143. return ret;
  144. out_err:
  145. dprintk("NFS: Couldn't create callback socket or server thread; "
  146. "err = %d\n", ret);
  147. nfs_callback_info.users--;
  148. goto out;
  149. }
  150. /*
  151. * Kill the callback thread if it's no longer being used.
  152. */
  153. void nfs_callback_down(void)
  154. {
  155. mutex_lock(&nfs_callback_mutex);
  156. nfs_callback_info.users--;
  157. if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
  158. kthread_stop(nfs_callback_info.task);
  159. svc_exit_thread(nfs_callback_info.rqst);
  160. nfs_callback_info.rqst = NULL;
  161. nfs_callback_info.task = NULL;
  162. }
  163. mutex_unlock(&nfs_callback_mutex);
  164. }
  165. static int check_gss_callback_principal(struct nfs_client *clp,
  166. struct svc_rqst *rqstp)
  167. {
  168. struct rpc_clnt *r = clp->cl_rpcclient;
  169. char *p = svc_gss_principal(rqstp);
  170. /*
  171. * It might just be a normal user principal, in which case
  172. * userspace won't bother to tell us the name at all.
  173. */
  174. if (p == NULL)
  175. return SVC_DENIED;
  176. /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
  177. if (memcmp(p, "nfs@", 4) != 0)
  178. return SVC_DENIED;
  179. p += 4;
  180. if (strcmp(p, r->cl_server) != 0)
  181. return SVC_DENIED;
  182. return SVC_OK;
  183. }
  184. static int nfs_callback_authenticate(struct svc_rqst *rqstp)
  185. {
  186. struct nfs_client *clp;
  187. RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
  188. int ret = SVC_OK;
  189. /* Don't talk to strangers */
  190. clp = nfs_find_client(svc_addr(rqstp), 4);
  191. if (clp == NULL)
  192. return SVC_DROP;
  193. dprintk("%s: %s NFSv4 callback!\n", __func__,
  194. svc_print_addr(rqstp, buf, sizeof(buf)));
  195. switch (rqstp->rq_authop->flavour) {
  196. case RPC_AUTH_NULL:
  197. if (rqstp->rq_proc != CB_NULL)
  198. ret = SVC_DENIED;
  199. break;
  200. case RPC_AUTH_UNIX:
  201. break;
  202. case RPC_AUTH_GSS:
  203. ret = check_gss_callback_principal(clp, rqstp);
  204. break;
  205. default:
  206. ret = SVC_DENIED;
  207. }
  208. nfs_put_client(clp);
  209. return ret;
  210. }
  211. /*
  212. * Define NFS4 callback program
  213. */
  214. static struct svc_version *nfs4_callback_version[] = {
  215. [1] = &nfs4_callback_version1,
  216. };
  217. static struct svc_stat nfs4_callback_stats;
  218. static struct svc_program nfs4_callback_program = {
  219. .pg_prog = NFS4_CALLBACK, /* RPC service number */
  220. .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */
  221. .pg_vers = nfs4_callback_version, /* version table */
  222. .pg_name = "NFSv4 callback", /* service name */
  223. .pg_class = "nfs", /* authentication class */
  224. .pg_stats = &nfs4_callback_stats,
  225. .pg_authenticate = nfs_callback_authenticate,
  226. };