4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/pathname.h>
29 #include <sys/errno.h>
30 #include <sys/cmn_err.h>
31 #include <sys/debug.h>
32 #include <sys/systm.h>
33 #include <sys/unistd.h>
35 #include <sys/socket.h>
36 #include <nfs/export.h>
37 #include <nfs/nfs_cmd.h>
39 #include <sys/sunddi.h>
41 #define NFSCMD_DR_TRYCNT 8
46 #define nextdp(dp) ((struct dirent *)((char *)(dp) + (dp)->d_reclen))
49 door_handle_t nfscmd_dh
;
51 static struct charset_cache
*nfscmd_charmap(exportinfo_t
*exi
,
56 nfscmd_args(uint_t did
)
58 mutex_enter(&nfscmd_lock
);
60 door_ki_rele(nfscmd_dh
);
61 nfscmd_dh
= door_ki_lookup(did
);
62 mutex_exit(&nfscmd_lock
);
68 mutex_init(&nfscmd_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
77 * nfscmd_send(arg, result)
79 * Send a command to the daemon listening on the door. The result is
80 * returned in the result pointer if the function return value is
81 * NFSCMD_ERR_SUCCESS. Otherwise it is the error value.
84 nfscmd_send(nfscmd_arg_t
*arg
, nfscmd_res_t
*res
)
93 mutex_enter(&nfscmd_lock
);
97 mutex_exit(&nfscmd_lock
);
101 * The rendezvous point has not been established yet !
102 * This could mean that either mountd(1m) has not yet
103 * been started or that _this_ routine nuked the door
104 * handle after receiving an EINTR for a REVOKED door.
106 * Returning NFSAUTH_DROP will cause the NFS client
107 * to retransmit the request, so let's try to be more
108 * rescillient and attempt for ntries before we bail.
110 if (++ntries
% NFSCMD_DR_TRYCNT
) {
114 return (NFSCMD_ERR_DROP
);
117 da
.data_ptr
= (char *)arg
;
118 da
.data_size
= sizeof (nfscmd_arg_t
);
121 da
.rbuf
= (char *)res
;
122 da
.rsize
= sizeof (nfscmd_res_t
);
124 switch (door_ki_upcall(dh
, &da
)) {
129 /* Need to retry a couple of times */
135 if (!door_ki_info(dh
, &di
)) {
136 if (di
.di_attributes
& DOOR_REVOKED
) {
138 * The server barfed and revoked
139 * the (existing) door on us; we
140 * want to wait to give smf(5) a
141 * chance to restart mountd(1m)
142 * and establish a new door handle.
144 mutex_enter(&nfscmd_lock
);
147 mutex_exit(&nfscmd_lock
);
153 * If the door was _not_ revoked on us,
154 * then more than likely we took an INTR,
155 * so we need to fail the operation.
160 * The only failure that can occur from getting
161 * the door info is EINVAL, so we let the code
170 * If we have a stale door handle, give smf a last
171 * chance to start it by sleeping for a little bit.
172 * If we're still hosed, we'll fail the call.
174 * Since we're going to reacquire the door handle
175 * upon the retry, we opt to sleep for a bit and
176 * _not_ to clear mountd_dh. If mountd restarted
177 * and was able to set mountd_dh, we should see
178 * the new instance; if not, we won't get caught
179 * up in the retry/DELAY loop.
187 res
->error
= NFSCMD_ERR_FAIL
;
194 * nfscmd_findmap(export, addr)
196 * Find a characterset map for the specified client address.
197 * First try to find a cached entry. If not successful,
198 * ask mountd daemon running in userland.
200 * For most of the clients this function is NOOP, since
201 * EX_CHARMAP flag won't be set.
203 struct charset_cache
*
204 nfscmd_findmap(struct exportinfo
*exi
, struct sockaddr
*sp
)
206 struct charset_cache
*charset
;
209 * In debug kernel we want to know about strayed nulls.
210 * In non-debug kernel we behave gracefully.
215 if (exi
== NULL
|| sp
== NULL
)
218 mutex_enter(&exi
->exi_lock
);
220 if (!(exi
->exi_export
.ex_flags
& EX_CHARMAP
)) {
221 mutex_exit(&exi
->exi_lock
);
225 for (charset
= exi
->exi_charset
;
227 charset
= charset
->next
) {
228 if (bcmp(sp
, &charset
->client_addr
,
229 sizeof (struct sockaddr
)) == 0)
232 mutex_exit(&exi
->exi_lock
);
234 /* the slooow way - ask daemon */
236 charset
= nfscmd_charmap(exi
, sp
);
242 * nfscmd_insert_charmap(export, addr, name)
244 * Insert a new character set conversion map into the export structure
245 * for the share. The entry has the IP address of the client and the
246 * character set name.
249 static struct charset_cache
*
250 nfscmd_insert_charmap(struct exportinfo
*exi
, struct sockaddr
*sp
, char *name
)
252 struct charset_cache
*charset
;
254 charset
= (struct charset_cache
*)
255 kmem_zalloc(sizeof (struct charset_cache
), KM_SLEEP
);
260 charset
->inbound
= kiconv_open("UTF-8", name
);
261 charset
->outbound
= kiconv_open(name
, "UTF-8");
263 charset
->client_addr
= *sp
;
264 mutex_enter(&exi
->exi_lock
);
265 charset
->next
= exi
->exi_charset
;
266 exi
->exi_charset
= charset
;
267 mutex_exit(&exi
->exi_lock
);
273 * nfscmd_charmap(response, sp, exi)
275 * Check to see if this client needs a character set conversion.
277 static struct charset_cache
*
278 nfscmd_charmap(exportinfo_t
*exi
, struct sockaddr
*sp
)
284 struct charset_cache
*charset
;
286 path
= exi
->exi_export
.ex_path
;
291 * nfscmd_findmap() did not find one in the cache so make
292 * the request to the daemon. We need to add the entry in
293 * either case since we want negative as well as
296 req
.cmd
= NFSCMD_CHARMAP_LOOKUP
;
297 req
.version
= NFSCMD_VERSION
;
298 req
.arg
.charmap
.addr
= *sp
;
299 (void) strncpy(req
.arg
.charmap
.path
, path
, MAXPATHLEN
);
300 bzero((caddr_t
)&res
, sizeof (nfscmd_res_t
));
301 ret
= nfscmd_send(&req
, &res
);
302 if (ret
== NFSCMD_ERR_SUCCESS
)
303 charset
= nfscmd_insert_charmap(exi
, sp
,
304 res
.result
.charmap
.codeset
);
306 charset
= nfscmd_insert_charmap(exi
, sp
, NULL
);
312 * nfscmd_convname(addr, export, name, inbound, size)
314 * Convert the given "name" string to the appropriate character set.
315 * If inbound is true, convert from the client character set to UTF-8.
316 * If inbound is false, convert from UTF-8 to the client characters set.
318 * In case of NFS v4 this is used for ill behaved clients, since
319 * according to the standard all file names should be utf-8 encoded
324 nfscmd_convname(struct sockaddr
*ca
, struct exportinfo
*exi
, char *name
,
325 int inbound
, size_t size
)
333 struct charset_cache
*charset
= NULL
;
335 charset
= nfscmd_findmap(exi
, ca
);
336 if (charset
== NULL
||
337 (charset
->inbound
== NULL
&& inbound
) ||
338 (charset
->outbound
== NULL
&& !inbound
))
341 /* make sure we have more than enough space */
342 newname
= kmem_zalloc(size
, KM_SLEEP
);
343 nsize
= strlen(name
);
347 ret
= kiconv(charset
->inbound
, &name
, &nsize
,
348 &holdname
, &osize
, &err
);
350 ret
= kiconv(charset
->outbound
, &name
, &nsize
,
351 &holdname
, &osize
, &err
);
352 if (ret
== (size_t)-1) {
353 kmem_free(newname
, size
);
361 * nfscmd_convdirent()
363 * There is only one entry in the data. Convert to new charset, if
364 * required and only return a success if it fits.
367 nfscmd_convdirent(struct sockaddr
*ca
, struct exportinfo
*exi
, char *data
,
368 size_t size
, enum nfsstat3
*error
)
377 struct charset_cache
*charset
;
379 charset
= nfscmd_findmap(exi
, ca
);
380 if (charset
== NULL
|| charset
->outbound
== (void *)~0)
383 newdata
= kmem_zalloc(size
, KM_SLEEP
);
385 nsize
= strlen(((struct dirent
*)data
)->d_name
);
387 bcopy(data
, newdata
, sizeof (struct dirent
));
389 iname
= ((struct dirent
*)data
)->d_name
;
390 oname
= ((struct dirent
*)newdata
)->d_name
;
392 ret
= kiconv(charset
->outbound
, &iname
, &nsize
, &oname
, &count
, &err
);
393 if (ret
== (size_t)-1) {
394 kmem_free(newdata
, size
);
398 *error
= NFS3ERR_NAMETOOLONG
;
403 ret
= strlen(((struct dirent
*)newdata
)->d_name
);
404 ((struct dirent
*)newdata
)->d_reclen
=
405 DIRENT_RECLEN(ret
+ 1);
411 * nfscmd_convdirplus(addr, export, data, nents, maxsize, ndata)
413 * Convert the dirents in data into a new list of dirents in ndata.
417 nfscmd_convdirplus(struct sockaddr
*ca
, struct exportinfo
*exi
, char *data
,
418 size_t nents
, size_t maxsize
, char **ndata
)
432 struct charset_cache
*charset
;
433 *ndata
= data
; /* return the data if no changes to make */
435 charset
= nfscmd_findmap(exi
, ca
);
437 if (charset
== NULL
|| charset
->outbound
== (void *)~0)
440 newdata
= kmem_zalloc(maxsize
, KM_SLEEP
);
443 dp
= (struct dirent
*)data
;
444 ndp
= (struct dirent
*)newdata
;
446 for (skipped
= 0, i
= 0; i
< nents
; i
++) {
448 * Copy the dp information if it fits. Then copy and
449 * convert the name in the entry.
451 if ((maxsize
- nsize
) < dp
->d_reclen
)
456 ilen
= strlen(iname
);
458 olen
= MIN(MAXNAMELEN
, maxsize
- nsize
);
459 ret
= kiconv(charset
->outbound
, &iname
, &ilen
, &oname
,
462 if (ret
== (size_t)-1) {
473 ilen
= MIN(MAXNAMELEN
, maxsize
- nsize
) - olen
;
474 ndp
->d_name
[ilen
] = '\0';
476 * What to do with other errors?
477 * For now, we return the unconverted string.
479 ndp
->d_reclen
= DIRENT_RECLEN(strlen(ndp
->d_name
) + 1);
480 nsize
+= ndp
->d_reclen
;
486 return (nents
- (i
+ skipped
));
490 * nfscmd_countents(data, len)
492 * How many dirents are there in the data buffer?
496 nfscmd_countents(char *data
, size_t len
)
498 struct dirent
*dp
= (struct dirent
*)data
;
503 for (nents
= 0, curlen
= 0; curlen
< len
; curlen
+= reclen
, nents
++) {
504 reclen
= dp
->d_reclen
;
511 * nfscmd_dropped_entrysize(dir, drop, nents)
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?
518 nfscmd_dropped_entrysize(struct dirent
*dir
, size_t drop
, size_t nents
)
523 for (i
= nents
- drop
; i
> 0 && dir
!= NULL
; i
--)
529 for (size
= 0, i
= 0; i
< drop
&& dir
!= NULL
; i
++) {
530 size
+= dir
->d_reclen
;