Print this page
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
OS-20 share_nfs(1m) charset handling is unreliable
OS-22 Page fault at nfscmd_dropped_entrysize+0x1e()
OS-23 NFSv2/3/4: READDIR responses are inconsistent when charset conversion fails
OS-24 rfs3_readdir(): Issues related to nfscmd_convdirent()
Reviewed by: Jan Kryl <jan.kryl@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/nfs/nfs_cmd.c
+++ new/usr/src/uts/common/fs/nfs/nfs_cmd.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
|
↓ open down ↓ |
10 lines elided |
↑ open up ↑ |
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 +
21 22 /*
22 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 24 * Use is subject to license terms.
24 25 */
25 26
27 +/*
28 + * Copyright 2018 Nexenta Systems, Inc.
29 + */
30 +
26 31 #include <sys/param.h>
27 32 #include <sys/types.h>
28 33 #include <sys/pathname.h>
29 34 #include <sys/errno.h>
30 35 #include <sys/cmn_err.h>
31 36 #include <sys/debug.h>
32 37 #include <sys/systm.h>
33 38 #include <sys/unistd.h>
34 39 #include <sys/door.h>
35 40 #include <sys/socket.h>
36 41 #include <nfs/export.h>
37 42 #include <nfs/nfs_cmd.h>
|
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
38 43 #include <sys/kmem.h>
39 44 #include <sys/sunddi.h>
40 45
41 46 #define NFSCMD_DR_TRYCNT 8
42 47
43 48 #ifdef nextdp
44 49 #undef nextdp
45 50 #endif
46 51 #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
47 52
48 -kmutex_t nfscmd_lock;
49 -door_handle_t nfscmd_dh;
53 +typedef struct nfscmd_globals {
54 + kmutex_t nfscmd_lock;
55 + door_handle_t nfscmd_dh;
56 +} nfscmd_globals_t;
50 57
58 +static zone_key_t nfscmd_zone_key;
59 +
51 60 static struct charset_cache *nfscmd_charmap(exportinfo_t *exi,
52 61 struct sockaddr *sp);
62 +static void *nfscmd_zone_init(zoneid_t);
63 +static void nfscmd_zone_fini(zoneid_t, void *);
53 64
54 -
55 65 void
56 66 nfscmd_args(uint_t did)
57 67 {
58 - mutex_enter(&nfscmd_lock);
59 - if (nfscmd_dh)
60 - door_ki_rele(nfscmd_dh);
61 - nfscmd_dh = door_ki_lookup(did);
62 - mutex_exit(&nfscmd_lock);
68 + nfscmd_globals_t *ncg = zone_getspecific(nfscmd_zone_key, curzone);
69 +
70 + mutex_enter(&ncg->nfscmd_lock);
71 + if (ncg->nfscmd_dh != NULL)
72 + door_ki_rele(ncg->nfscmd_dh);
73 + ncg->nfscmd_dh = door_ki_lookup(did);
74 + mutex_exit(&ncg->nfscmd_lock);
63 75 }
64 76
65 77 void
66 78 nfscmd_init(void)
67 79 {
68 - mutex_init(&nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
80 + zone_key_create(&nfscmd_zone_key, nfscmd_zone_init,
81 + NULL, nfscmd_zone_fini);
69 82 }
70 83
71 84 void
72 85 nfscmd_fini(void)
73 86 {
87 + (void) zone_key_delete(nfscmd_zone_key);
74 88 }
75 89
90 +/*ARGSUSED*/
91 +static void *
92 +nfscmd_zone_init(zoneid_t zoneid)
93 +{
94 + nfscmd_globals_t *ncg;
95 +
96 + ncg = kmem_zalloc(sizeof (*ncg), KM_SLEEP);
97 + mutex_init(&ncg->nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
98 +
99 + return (ncg);
100 +}
101 +
102 +/*ARGSUSED*/
103 +static void
104 +nfscmd_zone_fini(zoneid_t zoneid, void *data)
105 +{
106 + nfscmd_globals_t *ncg = data;
107 +
108 + mutex_destroy(&ncg->nfscmd_lock);
109 + if (ncg->nfscmd_dh)
110 + door_ki_rele(ncg->nfscmd_dh);
111 + kmem_free(ncg, sizeof (*ncg));
112 +}
113 +
76 114 /*
77 115 * nfscmd_send(arg, result)
78 116 *
79 117 * Send a command to the daemon listening on the door. The result is
80 118 * returned in the result pointer if the function return value is
81 119 * NFSCMD_ERR_SUCCESS. Otherwise it is the error value.
82 120 */
83 121 int
84 122 nfscmd_send(nfscmd_arg_t *arg, nfscmd_res_t *res)
85 123 {
86 124 door_handle_t dh;
87 125 door_arg_t da;
88 126 door_info_t di;
89 127 int ntries = 0;
90 128 int last = 0;
129 + nfscmd_globals_t *ncg = zone_getspecific(nfscmd_zone_key, curzone);
91 130
92 131 retry:
93 - mutex_enter(&nfscmd_lock);
94 - dh = nfscmd_dh;
132 + mutex_enter(&ncg->nfscmd_lock);
133 + dh = ncg->nfscmd_dh;
95 134 if (dh != NULL)
96 135 door_ki_hold(dh);
97 - mutex_exit(&nfscmd_lock);
136 + mutex_exit(&ncg->nfscmd_lock);
98 137
99 138 if (dh == NULL) {
100 139 /*
101 140 * The rendezvous point has not been established yet !
102 141 * This could mean that either mountd(1m) has not yet
103 142 * been started or that _this_ routine nuked the door
104 143 * handle after receiving an EINTR for a REVOKED door.
105 144 *
106 145 * Returning NFSAUTH_DROP will cause the NFS client
107 146 * to retransmit the request, so let's try to be more
108 147 * rescillient and attempt for ntries before we bail.
109 148 */
110 149 if (++ntries % NFSCMD_DR_TRYCNT) {
111 150 delay(hz);
112 151 goto retry;
113 152 }
114 153 return (NFSCMD_ERR_DROP);
115 154 }
116 155
117 156 da.data_ptr = (char *)arg;
118 157 da.data_size = sizeof (nfscmd_arg_t);
119 158 da.desc_ptr = NULL;
120 159 da.desc_num = 0;
121 160 da.rbuf = (char *)res;
122 161 da.rsize = sizeof (nfscmd_res_t);
123 162
124 163 switch (door_ki_upcall(dh, &da)) {
125 164 case 0:
126 165 /* Success */
127 166 break;
128 167 case EAGAIN:
129 168 /* Need to retry a couple of times */
130 169 door_ki_rele(dh);
131 170 delay(hz);
132 171 goto retry;
133 172 /* NOTREACHED */
|
↓ open down ↓ |
26 lines elided |
↑ open up ↑ |
134 173 case EINTR:
135 174 if (!door_ki_info(dh, &di)) {
136 175 if (di.di_attributes & DOOR_REVOKED) {
137 176 /*
138 177 * The server barfed and revoked
139 178 * the (existing) door on us; we
140 179 * want to wait to give smf(5) a
141 180 * chance to restart mountd(1m)
142 181 * and establish a new door handle.
143 182 */
144 - mutex_enter(&nfscmd_lock);
145 - if (dh == nfscmd_dh)
146 - nfscmd_dh = NULL;
147 - mutex_exit(&nfscmd_lock);
183 + mutex_enter(&ncg->nfscmd_lock);
184 + if (dh == ncg->nfscmd_dh)
185 + ncg->nfscmd_dh = NULL;
186 + mutex_exit(&ncg->nfscmd_lock);
148 187 door_ki_rele(dh);
149 188 delay(hz);
150 189 goto retry;
151 190 }
152 191 /*
153 192 * If the door was _not_ revoked on us,
154 193 * then more than likely we took an INTR,
155 194 * so we need to fail the operation.
156 195 */
157 196 door_ki_rele(dh);
158 197 }
159 198 /*
160 199 * The only failure that can occur from getting
161 200 * the door info is EINVAL, so we let the code
162 201 * below handle it.
163 202 */
164 203 /* FALLTHROUGH */
165 204
166 205 case EBADF:
167 206 case EINVAL:
168 207 default:
169 208 /*
170 209 * If we have a stale door handle, give smf a last
171 210 * chance to start it by sleeping for a little bit.
172 211 * If we're still hosed, we'll fail the call.
173 212 *
174 213 * Since we're going to reacquire the door handle
175 214 * upon the retry, we opt to sleep for a bit and
176 215 * _not_ to clear mountd_dh. If mountd restarted
177 216 * and was able to set mountd_dh, we should see
178 217 * the new instance; if not, we won't get caught
179 218 * up in the retry/DELAY loop.
180 219 */
181 220 door_ki_rele(dh);
182 221 if (!last) {
183 222 delay(hz);
184 223 last++;
185 224 goto retry;
186 225 }
187 226 res->error = NFSCMD_ERR_FAIL;
188 227 break;
189 228 }
190 229 return (res->error);
191 230 }
192 231
193 232 /*
194 233 * nfscmd_findmap(export, addr)
195 234 *
196 235 * Find a characterset map for the specified client address.
197 236 * First try to find a cached entry. If not successful,
198 237 * ask mountd daemon running in userland.
199 238 *
200 239 * For most of the clients this function is NOOP, since
201 240 * EX_CHARMAP flag won't be set.
202 241 */
203 242 struct charset_cache *
204 243 nfscmd_findmap(struct exportinfo *exi, struct sockaddr *sp)
205 244 {
206 245 struct charset_cache *charset;
207 246
208 247 /*
209 248 * In debug kernel we want to know about strayed nulls.
210 249 * In non-debug kernel we behave gracefully.
211 250 */
212 251 ASSERT(exi != NULL);
213 252 ASSERT(sp != NULL);
214 253
215 254 if (exi == NULL || sp == NULL)
216 255 return (NULL);
217 256
218 257 mutex_enter(&exi->exi_lock);
219 258
220 259 if (!(exi->exi_export.ex_flags & EX_CHARMAP)) {
221 260 mutex_exit(&exi->exi_lock);
222 261 return (NULL);
223 262 }
224 263
225 264 for (charset = exi->exi_charset;
226 265 charset != NULL;
227 266 charset = charset->next) {
228 267 if (bcmp(sp, &charset->client_addr,
229 268 sizeof (struct sockaddr)) == 0)
230 269 break;
231 270 }
232 271 mutex_exit(&exi->exi_lock);
233 272
234 273 /* the slooow way - ask daemon */
235 274 if (charset == NULL)
236 275 charset = nfscmd_charmap(exi, sp);
237 276
238 277 return (charset);
239 278 }
240 279
241 280 /*
242 281 * nfscmd_insert_charmap(export, addr, name)
243 282 *
244 283 * Insert a new character set conversion map into the export structure
245 284 * for the share. The entry has the IP address of the client and the
246 285 * character set name.
247 286 */
248 287
|
↓ open down ↓ |
91 lines elided |
↑ open up ↑ |
249 288 static struct charset_cache *
250 289 nfscmd_insert_charmap(struct exportinfo *exi, struct sockaddr *sp, char *name)
251 290 {
252 291 struct charset_cache *charset;
253 292
254 293 charset = (struct charset_cache *)
255 294 kmem_zalloc(sizeof (struct charset_cache), KM_SLEEP);
256 295
257 296 if (charset == NULL)
258 297 return (NULL);
259 - if (name != NULL) {
260 - charset->inbound = kiconv_open("UTF-8", name);
261 - charset->outbound = kiconv_open(name, "UTF-8");
262 - }
298 +
299 + charset->inbound = kiconv_open("UTF-8", name);
300 + charset->outbound = kiconv_open(name, "UTF-8");
301 +
263 302 charset->client_addr = *sp;
264 303 mutex_enter(&exi->exi_lock);
265 304 charset->next = exi->exi_charset;
266 305 exi->exi_charset = charset;
267 306 mutex_exit(&exi->exi_lock);
268 307
269 308 return (charset);
270 309 }
271 310
272 311 /*
273 312 * nfscmd_charmap(response, sp, exi)
274 313 *
275 314 * Check to see if this client needs a character set conversion.
276 315 */
277 316 static struct charset_cache *
278 317 nfscmd_charmap(exportinfo_t *exi, struct sockaddr *sp)
279 318 {
280 319 nfscmd_arg_t req;
281 320 int ret;
282 321 char *path;
283 322 nfscmd_res_t res;
284 323 struct charset_cache *charset;
285 324
286 325 path = exi->exi_export.ex_path;
287 326 if (path == NULL)
288 327 return (NULL);
289 328
290 329 /*
291 330 * nfscmd_findmap() did not find one in the cache so make
292 331 * the request to the daemon. We need to add the entry in
293 332 * either case since we want negative as well as
294 333 * positive cacheing.
295 334 */
296 335 req.cmd = NFSCMD_CHARMAP_LOOKUP;
297 336 req.version = NFSCMD_VERSION;
298 337 req.arg.charmap.addr = *sp;
299 338 (void) strncpy(req.arg.charmap.path, path, MAXPATHLEN);
300 339 bzero((caddr_t)&res, sizeof (nfscmd_res_t));
301 340 ret = nfscmd_send(&req, &res);
302 341 if (ret == NFSCMD_ERR_SUCCESS)
303 342 charset = nfscmd_insert_charmap(exi, sp,
304 343 res.result.charmap.codeset);
305 344 else
306 345 charset = nfscmd_insert_charmap(exi, sp, NULL);
307 346
308 347 return (charset);
309 348 }
310 349
311 350 /*
312 351 * nfscmd_convname(addr, export, name, inbound, size)
313 352 *
314 353 * Convert the given "name" string to the appropriate character set.
315 354 * If inbound is true, convert from the client character set to UTF-8.
316 355 * If inbound is false, convert from UTF-8 to the client characters set.
317 356 *
318 357 * In case of NFS v4 this is used for ill behaved clients, since
319 358 * according to the standard all file names should be utf-8 encoded
320 359 * on client-side.
321 360 */
322 361
323 362 char *
324 363 nfscmd_convname(struct sockaddr *ca, struct exportinfo *exi, char *name,
325 364 int inbound, size_t size)
326 365 {
|
↓ open down ↓ |
54 lines elided |
↑ open up ↑ |
327 366 char *newname;
328 367 char *holdname;
329 368 int err;
330 369 int ret;
331 370 size_t nsize;
332 371 size_t osize;
333 372 struct charset_cache *charset = NULL;
334 373
335 374 charset = nfscmd_findmap(exi, ca);
336 375 if (charset == NULL ||
337 - (charset->inbound == NULL && inbound) ||
338 - (charset->outbound == NULL && !inbound))
376 + (charset->inbound == (kiconv_t)-1 && inbound) ||
377 + (charset->outbound == (kiconv_t)-1 && !inbound))
339 378 return (name);
340 379
341 380 /* make sure we have more than enough space */
342 381 newname = kmem_zalloc(size, KM_SLEEP);
343 382 nsize = strlen(name);
344 383 osize = size;
345 384 holdname = newname;
346 385 if (inbound)
347 386 ret = kiconv(charset->inbound, &name, &nsize,
348 387 &holdname, &osize, &err);
349 388 else
350 389 ret = kiconv(charset->outbound, &name, &nsize,
351 390 &holdname, &osize, &err);
352 391 if (ret == (size_t)-1) {
353 392 kmem_free(newname, size);
354 393 newname = NULL;
355 394 }
356 395
357 396 return (newname);
358 -}
359 -
360 -/*
361 - * nfscmd_convdirent()
362 - *
363 - * There is only one entry in the data. Convert to new charset, if
364 - * required and only return a success if it fits.
365 - */
366 -char *
367 -nfscmd_convdirent(struct sockaddr *ca, struct exportinfo *exi, char *data,
368 - size_t size, enum nfsstat3 *error)
369 -{
370 - char *newdata;
371 - size_t ret;
372 - size_t nsize;
373 - size_t count;
374 - int err = 0;
375 - char *iname;
376 - char *oname;
377 - struct charset_cache *charset;
378 -
379 - charset = nfscmd_findmap(exi, ca);
380 - if (charset == NULL || charset->outbound == (void *)~0)
381 - return (data);
382 -
383 - newdata = kmem_zalloc(size, KM_SLEEP);
384 -
385 - nsize = strlen(((struct dirent64 *)data)->d_name);
386 - count = size;
387 - bcopy(data, newdata, sizeof (struct dirent64));
388 -
389 - iname = ((struct dirent64 *)data)->d_name;
390 - oname = ((struct dirent64 *)newdata)->d_name;
391 -
392 - ret = kiconv(charset->outbound, &iname, &nsize, &oname, &count, &err);
393 - if (ret == (size_t)-1) {
394 - kmem_free(newdata, size);
395 - newdata = NULL;
396 - if (err == E2BIG) {
397 - if (error != NULL)
398 - *error = NFS3ERR_NAMETOOLONG;
399 - } else {
400 - newdata = data;
401 - }
402 - } else {
403 - ret = strlen(((struct dirent64 *)newdata)->d_name);
404 - ((struct dirent64 *)newdata)->d_reclen =
405 - DIRENT64_RECLEN(ret + 1);
406 - }
407 - return (newdata);
408 -}
409 -
410 -/*
411 - * nfscmd_convdirplus(addr, export, data, nents, maxsize, ndata)
412 - *
413 - * Convert the dirents in data into a new list of dirents in ndata.
414 - */
415 -
416 -size_t
417 -nfscmd_convdirplus(struct sockaddr *ca, struct exportinfo *exi, char *data,
418 - size_t nents, size_t maxsize, char **ndata)
419 -{
420 - char *newdata;
421 - size_t nsize;
422 - struct dirent64 *dp;
423 - struct dirent64 *ndp;
424 - size_t i;
425 - size_t ret;
426 - char *iname;
427 - char *oname;
428 - size_t ilen;
429 - size_t olen;
430 - int err;
431 - size_t skipped;
432 - struct charset_cache *charset;
433 - *ndata = data; /* return the data if no changes to make */
434 -
435 - charset = nfscmd_findmap(exi, ca);
436 -
437 - if (charset == NULL || charset->outbound == (void *)~0)
438 - return (0);
439 -
440 - newdata = kmem_zalloc(maxsize, KM_SLEEP);
441 - nsize = 0;
442 -
443 - dp = (struct dirent64 *)data;
444 - ndp = (struct dirent64 *)newdata;
445 -
446 - for (skipped = 0, i = 0; i < nents; i++) {
447 - /*
448 - * Copy the dp information if it fits. Then copy and
449 - * convert the name in the entry.
450 - */
451 - if ((maxsize - nsize) < dp->d_reclen)
452 - /* doesn't fit */
453 - break;
454 - *ndp = *dp;
455 - iname = dp->d_name;
456 - ilen = strlen(iname);
457 - oname = ndp->d_name;
458 - olen = MIN(MAXNAMELEN, maxsize - nsize);
459 - ret = kiconv(charset->outbound, &iname, &ilen, &oname,
460 - &olen, &err);
461 -
462 - if (ret == (size_t)-1) {
463 - switch (err) {
464 - default:
465 - case E2BIG:
466 - break;
467 - case EILSEQ:
468 - skipped++;
469 - dp = nextdp(dp);
470 - continue;
471 - }
472 - }
473 - ilen = MIN(MAXNAMELEN, maxsize - nsize) - olen;
474 - ndp->d_name[ilen] = '\0';
475 - /*
476 - * What to do with other errors?
477 - * For now, we return the unconverted string.
478 - */
479 - ndp->d_reclen = DIRENT64_RECLEN(strlen(ndp->d_name) + 1);
480 - nsize += ndp->d_reclen;
481 - dp = nextdp(dp);
482 - ndp = nextdp(ndp);
483 - }
484 -
485 - *ndata = newdata;
486 - return (nents - (i + skipped));
487 -}
488 -
489 -/*
490 - * nfscmd_countents(data, len)
491 - *
492 - * How many dirents are there in the data buffer?
493 - */
494 -
495 -size_t
496 -nfscmd_countents(char *data, size_t len)
497 -{
498 - struct dirent64 *dp = (struct dirent64 *)data;
499 - size_t curlen;
500 - size_t reclen;
501 - size_t nents;
502 -
503 - for (nents = 0, curlen = 0; curlen < len; curlen += reclen, nents++) {
504 - reclen = dp->d_reclen;
505 - dp = nextdp(dp);
506 - }
507 - return (nents);
508 -}
509 -
510 -/*
511 - * nfscmd_dropped_entrysize(dir, drop, nents)
512 - *
513 - * We need to drop "drop" entries from dir in order to fit in the
514 - * buffer. How much do we reduce the overall size by?
515 - */
516 -
517 -size_t
518 -nfscmd_dropped_entrysize(struct dirent64 *dir, size_t drop, size_t nents)
519 -{
520 - size_t size;
521 - size_t i;
522 -
523 - for (i = nents - drop; i > 0 && dir != NULL; i--)
524 - dir = nextdp(dir);
525 -
526 - if (dir == NULL)
527 - return (0);
528 -
529 - for (size = 0, i = 0; i < drop && dir != NULL; i++) {
530 - size += dir->d_reclen;
531 - dir = nextdp(dir);
532 - }
533 - return (size);
534 397 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX