]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - fs/nfs/callback.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-2.6
[linux-2.6.git] / fs / nfs / callback.c
1 /*
2  * linux/fs/nfs/callback.c
3  *
4  * Copyright (C) 2004 Trond Myklebust
5  *
6  * NFSv4 callback handling
7  */
8
9 #include <linux/config.h>
10 #include <linux/completion.h>
11 #include <linux/ip.h>
12 #include <linux/module.h>
13 #include <linux/smp_lock.h>
14 #include <linux/sunrpc/svc.h>
15 #include <linux/sunrpc/svcsock.h>
16 #include <linux/nfs_fs.h>
17
18 #include <net/inet_sock.h>
19
20 #include "nfs4_fs.h"
21 #include "callback.h"
22
23 #define NFSDBG_FACILITY NFSDBG_CALLBACK
24
25 struct nfs_callback_data {
26         unsigned int users;
27         struct svc_serv *serv;
28         pid_t pid;
29         struct completion started;
30         struct completion stopped;
31 };
32
33 static struct nfs_callback_data nfs_callback_info;
34 static DECLARE_MUTEX(nfs_callback_sema);
35 static struct svc_program nfs4_callback_program;
36
37 unsigned short nfs_callback_tcpport;
38
39 /*
40  * This is the callback kernel thread.
41  */
42 static void nfs_callback_svc(struct svc_rqst *rqstp)
43 {
44         struct svc_serv *serv = rqstp->rq_server;
45         int err;
46
47         __module_get(THIS_MODULE);
48         lock_kernel();
49
50         nfs_callback_info.pid = current->pid;
51         daemonize("nfsv4-svc");
52         /* Process request with signals blocked, but allow SIGKILL.  */
53         allow_signal(SIGKILL);
54
55         complete(&nfs_callback_info.started);
56
57         while (nfs_callback_info.users != 0 || !signalled()) {
58                 /*
59                  * Listen for a request on the socket
60                  */
61                 err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT);
62                 if (err == -EAGAIN || err == -EINTR)
63                         continue;
64                 if (err < 0) {
65                         printk(KERN_WARNING
66                                         "%s: terminating on error %d\n",
67                                         __FUNCTION__, -err);
68                         break;
69                 }
70                 dprintk("%s: request from %u.%u.%u.%u\n", __FUNCTION__,
71                                 NIPQUAD(rqstp->rq_addr.sin_addr.s_addr));
72                 svc_process(serv, rqstp);
73         }
74
75         nfs_callback_info.pid = 0;
76         complete(&nfs_callback_info.stopped);
77         unlock_kernel();
78         module_put_and_exit(0);
79 }
80
81 /*
82  * Bring up the server process if it is not already up.
83  */
84 int nfs_callback_up(void)
85 {
86         struct svc_serv *serv;
87         struct svc_sock *svsk;
88         int ret = 0;
89
90         lock_kernel();
91         down(&nfs_callback_sema);
92         if (nfs_callback_info.users++ || nfs_callback_info.pid != 0)
93                 goto out;
94         init_completion(&nfs_callback_info.started);
95         init_completion(&nfs_callback_info.stopped);
96         serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE);
97         ret = -ENOMEM;
98         if (!serv)
99                 goto out_err;
100         /* FIXME: We don't want to register this socket with the portmapper */
101         ret = svc_makesock(serv, IPPROTO_TCP, 0);
102         if (ret < 0)
103                 goto out_destroy;
104         if (!list_empty(&serv->sv_permsocks)) {
105                 svsk = list_entry(serv->sv_permsocks.next,
106                                 struct svc_sock, sk_list);
107                 nfs_callback_tcpport = ntohs(inet_sk(svsk->sk_sk)->sport);
108                 dprintk ("Callback port = 0x%x\n", nfs_callback_tcpport);
109         } else
110                 BUG();
111         ret = svc_create_thread(nfs_callback_svc, serv);
112         if (ret < 0)
113                 goto out_destroy;
114         nfs_callback_info.serv = serv;
115         wait_for_completion(&nfs_callback_info.started);
116 out:
117         up(&nfs_callback_sema);
118         unlock_kernel();
119         return ret;
120 out_destroy:
121         svc_destroy(serv);
122 out_err:
123         nfs_callback_info.users--;
124         goto out;
125 }
126
127 /*
128  * Kill the server process if it is not already up.
129  */
130 int nfs_callback_down(void)
131 {
132         int ret = 0;
133
134         lock_kernel();
135         down(&nfs_callback_sema);
136         if (--nfs_callback_info.users || nfs_callback_info.pid == 0)
137                 goto out;
138         kill_proc(nfs_callback_info.pid, SIGKILL, 1);
139         wait_for_completion(&nfs_callback_info.stopped);
140 out:
141         up(&nfs_callback_sema);
142         unlock_kernel();
143         return ret;
144 }
145
146 static int nfs_callback_authenticate(struct svc_rqst *rqstp)
147 {
148         struct in_addr *addr = &rqstp->rq_addr.sin_addr;
149         struct nfs4_client *clp;
150
151         /* Don't talk to strangers */
152         clp = nfs4_find_client(addr);
153         if (clp == NULL)
154                 return SVC_DROP;
155         dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
156         nfs4_put_client(clp);
157         switch (rqstp->rq_authop->flavour) {
158                 case RPC_AUTH_NULL:
159                         if (rqstp->rq_proc != CB_NULL)
160                                 return SVC_DENIED;
161                         break;
162                 case RPC_AUTH_UNIX:
163                         break;
164                 case RPC_AUTH_GSS:
165                         /* FIXME: RPCSEC_GSS handling? */
166                 default:
167                         return SVC_DENIED;
168         }
169         return SVC_OK;
170 }
171
172 /*
173  * Define NFS4 callback program
174  */
175 extern struct svc_version nfs4_callback_version1;
176
177 static struct svc_version *nfs4_callback_version[] = {
178         [1] = &nfs4_callback_version1,
179 };
180
181 static struct svc_stat nfs4_callback_stats;
182
183 static struct svc_program nfs4_callback_program = {
184         .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
185         .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
186         .pg_vers = nfs4_callback_version,               /* version table */
187         .pg_name = "NFSv4 callback",                    /* service name */
188         .pg_class = "nfs",                              /* authentication class */
189         .pg_stats = &nfs4_callback_stats,
190         .pg_authenticate = nfs_callback_authenticate,
191 };