diff options
Diffstat (limited to 'fs/lockd/mon.c')
-rw-r--r-- | fs/lockd/mon.c | 133 |
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 { | |||
47 | static struct rpc_clnt * nsm_create(void); | 47 | static struct rpc_clnt * nsm_create(void); |
48 | 48 | ||
49 | static struct rpc_program nsm_program; | 49 | static struct rpc_program nsm_program; |
50 | static LIST_HEAD(nsm_handles); | ||
51 | static DEFINE_SPINLOCK(nsm_lock); | ||
50 | 52 | ||
51 | /* | 53 | /* |
52 | * Local NSM state | 54 | * Local NSM state |
53 | */ | 55 | */ |
54 | int nsm_local_state; | 56 | int nsm_local_state; |
55 | 57 | ||
58 | static 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 | |||
65 | static 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 | |||
79 | static 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 | */ | ||
220 | struct 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 | |||
239 | retry: | ||
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 | |||
277 | found: | ||
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 | */ | ||
287 | void 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 | */ |