aboutsummaryrefslogtreecommitdiffstats
path: root/fs/lockd/mon.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/lockd/mon.c')
-rw-r--r--fs/lockd/mon.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 81e1cc14246f..8e68e799293c 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -47,12 +47,51 @@ struct nsm_res {
47static struct rpc_clnt * nsm_create(void); 47static struct rpc_clnt * nsm_create(void);
48 48
49static struct rpc_program nsm_program; 49static struct rpc_program nsm_program;
50static LIST_HEAD(nsm_handles);
51static DEFINE_SPINLOCK(nsm_lock);
50 52
51/* 53/*
52 * Local NSM state 54 * Local NSM state
53 */ 55 */
54int nsm_local_state; 56int nsm_local_state;
55 57
58static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf,
59 const size_t len)
60{
61 const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
62 snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
63}
64
65static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf,
66 const size_t len)
67{
68 const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
69
70 if (ipv6_addr_v4mapped(&sin6->sin6_addr))
71 snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
72 else if (sin6->sin6_scope_id != 0)
73 snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
74 sin6->sin6_scope_id);
75 else
76 snprintf(buf, len, "%pI6", &sin6->sin6_addr);
77}
78
79static void nsm_display_address(const struct sockaddr *sap,
80 char *buf, const size_t len)
81{
82 switch (sap->sa_family) {
83 case AF_INET:
84 nsm_display_ipv4_address(sap, buf, len);
85 break;
86 case AF_INET6:
87 nsm_display_ipv6_address(sap, buf, len);
88 break;
89 default:
90 snprintf(buf, len, "unsupported address family");
91 break;
92 }
93}
94
56/* 95/*
57 * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls 96 * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls
58 */ 97 */
@@ -162,6 +201,100 @@ void nsm_unmonitor(const struct nlm_host *host)
162 } 201 }
163} 202}
164 203
204/**
205 * nsm_find - Find or create a cached nsm_handle
206 * @sap: pointer to socket address of handle to find
207 * @salen: length of socket address
208 * @hostname: pointer to C string containing hostname to find
209 * @hostname_len: length of C string
210 * @create: one means create new handle if not found in cache
211 *
212 * Behavior is modulated by the global nsm_use_hostnames variable
213 * and by the @create argument.
214 *
215 * Returns a cached nsm_handle after bumping its ref count, or if
216 * @create is set, returns a fresh nsm_handle if a handle that
217 * matches @sap and/or @hostname cannot be found in the handle cache.
218 * Returns NULL if an error occurs.
219 */
220struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen,
221 const char *hostname, const size_t hostname_len,
222 const int create)
223{
224 struct nsm_handle *nsm = NULL;
225 struct nsm_handle *pos;
226
227 if (!sap)
228 return NULL;
229
230 if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
231 if (printk_ratelimit()) {
232 printk(KERN_WARNING "Invalid hostname \"%.*s\" "
233 "in NFS lock request\n",
234 (int)hostname_len, hostname);
235 }
236 return NULL;
237 }
238
239retry:
240 spin_lock(&nsm_lock);
241 list_for_each_entry(pos, &nsm_handles, sm_link) {
242
243 if (hostname && nsm_use_hostnames) {
244 if (strlen(pos->sm_name) != hostname_len
245 || memcmp(pos->sm_name, hostname, hostname_len))
246 continue;
247 } else if (!nlm_cmp_addr(nsm_addr(pos), sap))
248 continue;
249 atomic_inc(&pos->sm_count);
250 kfree(nsm);
251 nsm = pos;
252 goto found;
253 }
254 if (nsm) {
255 list_add(&nsm->sm_link, &nsm_handles);
256 goto found;
257 }
258 spin_unlock(&nsm_lock);
259
260 if (!create)
261 return NULL;
262
263 nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
264 if (nsm == NULL)
265 return NULL;
266
267 memcpy(nsm_addr(nsm), sap, salen);
268 nsm->sm_addrlen = salen;
269 nsm->sm_name = (char *) (nsm + 1);
270 memcpy(nsm->sm_name, hostname, hostname_len);
271 nsm->sm_name[hostname_len] = '\0';
272 nsm_display_address((struct sockaddr *)&nsm->sm_addr,
273 nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
274 atomic_set(&nsm->sm_count, 1);
275 goto retry;
276
277found:
278 spin_unlock(&nsm_lock);
279 return nsm;
280}
281
282/**
283 * nsm_release - Release an NSM handle
284 * @nsm: pointer to handle to be released
285 *
286 */
287void nsm_release(struct nsm_handle *nsm)
288{
289 if (!nsm)
290 return;
291 if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
292 list_del(&nsm->sm_link);
293 spin_unlock(&nsm_lock);
294 kfree(nsm);
295 }
296}
297
165/* 298/*
166 * Create NSM client for the local host 299 * Create NSM client for the local host
167 */ 300 */