xref: /onnv-gate/usr/src/uts/common/fs/nfs/nfs_cmd.c (revision 11632:32a585d893ca)
17961SNatalie.Li@Sun.COM /*
27961SNatalie.Li@Sun.COM  * CDDL HEADER START
37961SNatalie.Li@Sun.COM  *
47961SNatalie.Li@Sun.COM  * The contents of this file are subject to the terms of the
57961SNatalie.Li@Sun.COM  * Common Development and Distribution License (the "License").
67961SNatalie.Li@Sun.COM  * You may not use this file except in compliance with the License.
77961SNatalie.Li@Sun.COM  *
87961SNatalie.Li@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97961SNatalie.Li@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107961SNatalie.Li@Sun.COM  * See the License for the specific language governing permissions
117961SNatalie.Li@Sun.COM  * and limitations under the License.
127961SNatalie.Li@Sun.COM  *
137961SNatalie.Li@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147961SNatalie.Li@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157961SNatalie.Li@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167961SNatalie.Li@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177961SNatalie.Li@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187961SNatalie.Li@Sun.COM  *
197961SNatalie.Li@Sun.COM  * CDDL HEADER END
207961SNatalie.Li@Sun.COM  */
217961SNatalie.Li@Sun.COM /*
22*11632SJan.Kryl@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
237961SNatalie.Li@Sun.COM  * Use is subject to license terms.
247961SNatalie.Li@Sun.COM  */
257961SNatalie.Li@Sun.COM 
267961SNatalie.Li@Sun.COM #include <sys/param.h>
277961SNatalie.Li@Sun.COM #include <sys/types.h>
287961SNatalie.Li@Sun.COM #include <sys/pathname.h>
297961SNatalie.Li@Sun.COM #include <sys/errno.h>
307961SNatalie.Li@Sun.COM #include <sys/cmn_err.h>
317961SNatalie.Li@Sun.COM #include <sys/debug.h>
327961SNatalie.Li@Sun.COM #include <sys/systm.h>
337961SNatalie.Li@Sun.COM #include <sys/unistd.h>
347961SNatalie.Li@Sun.COM #include <sys/door.h>
357961SNatalie.Li@Sun.COM #include <sys/socket.h>
367961SNatalie.Li@Sun.COM #include <nfs/export.h>
377961SNatalie.Li@Sun.COM #include <nfs/nfs_cmd.h>
387961SNatalie.Li@Sun.COM #include <sys/kmem.h>
397961SNatalie.Li@Sun.COM #include <sys/sunddi.h>
407961SNatalie.Li@Sun.COM 
417961SNatalie.Li@Sun.COM #define	NFSCMD_DR_TRYCNT	8
427961SNatalie.Li@Sun.COM 
437961SNatalie.Li@Sun.COM #ifdef nextdp
447961SNatalie.Li@Sun.COM #undef nextdp
457961SNatalie.Li@Sun.COM #endif
467961SNatalie.Li@Sun.COM #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
477961SNatalie.Li@Sun.COM 
487961SNatalie.Li@Sun.COM kmutex_t	nfscmd_lock;
497961SNatalie.Li@Sun.COM door_handle_t   nfscmd_dh;
507961SNatalie.Li@Sun.COM 
51*11632SJan.Kryl@Sun.COM static struct charset_cache *nfscmd_charmap(exportinfo_t *exi,
52*11632SJan.Kryl@Sun.COM     struct sockaddr *sp);
53*11632SJan.Kryl@Sun.COM 
54*11632SJan.Kryl@Sun.COM 
557961SNatalie.Li@Sun.COM void
nfscmd_args(uint_t did)567961SNatalie.Li@Sun.COM nfscmd_args(uint_t did)
577961SNatalie.Li@Sun.COM {
587961SNatalie.Li@Sun.COM 	mutex_enter(&nfscmd_lock);
597961SNatalie.Li@Sun.COM 	if (nfscmd_dh)
607961SNatalie.Li@Sun.COM 		door_ki_rele(nfscmd_dh);
617961SNatalie.Li@Sun.COM 	nfscmd_dh = door_ki_lookup(did);
627961SNatalie.Li@Sun.COM 	mutex_exit(&nfscmd_lock);
637961SNatalie.Li@Sun.COM }
647961SNatalie.Li@Sun.COM 
657961SNatalie.Li@Sun.COM void
nfscmd_init(void)667961SNatalie.Li@Sun.COM nfscmd_init(void)
677961SNatalie.Li@Sun.COM {
687961SNatalie.Li@Sun.COM 	mutex_init(&nfscmd_lock, NULL, MUTEX_DEFAULT, NULL);
697961SNatalie.Li@Sun.COM }
707961SNatalie.Li@Sun.COM 
717961SNatalie.Li@Sun.COM void
nfscmd_fini(void)727961SNatalie.Li@Sun.COM nfscmd_fini(void)
737961SNatalie.Li@Sun.COM {
747961SNatalie.Li@Sun.COM }
757961SNatalie.Li@Sun.COM 
767961SNatalie.Li@Sun.COM /*
777961SNatalie.Li@Sun.COM  * nfscmd_send(arg, result)
787961SNatalie.Li@Sun.COM  *
79*11632SJan.Kryl@Sun.COM  * Send a command to the daemon listening on the door. The result is
80*11632SJan.Kryl@Sun.COM  * returned in the result pointer if the function return value is
81*11632SJan.Kryl@Sun.COM  * NFSCMD_ERR_SUCCESS. Otherwise it is the error value.
827961SNatalie.Li@Sun.COM  */
837961SNatalie.Li@Sun.COM int
nfscmd_send(nfscmd_arg_t * arg,nfscmd_res_t * res)847961SNatalie.Li@Sun.COM nfscmd_send(nfscmd_arg_t *arg, nfscmd_res_t *res)
857961SNatalie.Li@Sun.COM {
867961SNatalie.Li@Sun.COM 	door_handle_t	dh;
877961SNatalie.Li@Sun.COM 	door_arg_t	da;
887961SNatalie.Li@Sun.COM 	door_info_t	di;
897961SNatalie.Li@Sun.COM 	int		ntries = 0;
907961SNatalie.Li@Sun.COM 	int		last = 0;
917961SNatalie.Li@Sun.COM 
927961SNatalie.Li@Sun.COM retry:
937961SNatalie.Li@Sun.COM 	mutex_enter(&nfscmd_lock);
947961SNatalie.Li@Sun.COM 	dh = nfscmd_dh;
957961SNatalie.Li@Sun.COM 	if (dh != NULL)
967961SNatalie.Li@Sun.COM 		door_ki_hold(dh);
977961SNatalie.Li@Sun.COM 	mutex_exit(&nfscmd_lock);
987961SNatalie.Li@Sun.COM 
997961SNatalie.Li@Sun.COM 	if (dh == NULL) {
1007961SNatalie.Li@Sun.COM 		/*
1017961SNatalie.Li@Sun.COM 		 * The rendezvous point has not been established yet !
1027961SNatalie.Li@Sun.COM 		 * This could mean that either mountd(1m) has not yet
1037961SNatalie.Li@Sun.COM 		 * been started or that _this_ routine nuked the door
1047961SNatalie.Li@Sun.COM 		 * handle after receiving an EINTR for a REVOKED door.
1057961SNatalie.Li@Sun.COM 		 *
1067961SNatalie.Li@Sun.COM 		 * Returning NFSAUTH_DROP will cause the NFS client
1077961SNatalie.Li@Sun.COM 		 * to retransmit the request, so let's try to be more
1087961SNatalie.Li@Sun.COM 		 * rescillient and attempt for ntries before we bail.
1097961SNatalie.Li@Sun.COM 		 */
1107961SNatalie.Li@Sun.COM 		if (++ntries % NFSCMD_DR_TRYCNT) {
1117961SNatalie.Li@Sun.COM 			delay(hz);
1127961SNatalie.Li@Sun.COM 			goto retry;
1137961SNatalie.Li@Sun.COM 		}
1147961SNatalie.Li@Sun.COM 		return (NFSCMD_ERR_DROP);
1157961SNatalie.Li@Sun.COM 	}
1167961SNatalie.Li@Sun.COM 
1177961SNatalie.Li@Sun.COM 	da.data_ptr = (char *)arg;
1187961SNatalie.Li@Sun.COM 	da.data_size = sizeof (nfscmd_arg_t);
1197961SNatalie.Li@Sun.COM 	da.desc_ptr = NULL;
1207961SNatalie.Li@Sun.COM 	da.desc_num = 0;
1217961SNatalie.Li@Sun.COM 	da.rbuf = (char *)res;
1227961SNatalie.Li@Sun.COM 	da.rsize = sizeof (nfscmd_res_t);
1237961SNatalie.Li@Sun.COM 
1247961SNatalie.Li@Sun.COM 	switch (door_ki_upcall(dh, &da)) {
1257961SNatalie.Li@Sun.COM 	case 0:
1267961SNatalie.Li@Sun.COM 		/* Success */
1277961SNatalie.Li@Sun.COM 		break;
1287961SNatalie.Li@Sun.COM 	case EAGAIN:
1297961SNatalie.Li@Sun.COM 		/* Need to retry a couple of times */
1307961SNatalie.Li@Sun.COM 		door_ki_rele(dh);
1317961SNatalie.Li@Sun.COM 		delay(hz);
1327961SNatalie.Li@Sun.COM 		goto retry;
1337961SNatalie.Li@Sun.COM 		/* NOTREACHED */
1347961SNatalie.Li@Sun.COM 	case EINTR:
1357961SNatalie.Li@Sun.COM 		if (!door_ki_info(dh, &di)) {
1367961SNatalie.Li@Sun.COM 			if (di.di_attributes & DOOR_REVOKED) {
1377961SNatalie.Li@Sun.COM 				/*
1387961SNatalie.Li@Sun.COM 				 * The server barfed and revoked
1397961SNatalie.Li@Sun.COM 				 * the (existing) door on us; we
1407961SNatalie.Li@Sun.COM 				 * want to wait to give smf(5) a
1417961SNatalie.Li@Sun.COM 				 * chance to restart mountd(1m)
1427961SNatalie.Li@Sun.COM 				 * and establish a new door handle.
1437961SNatalie.Li@Sun.COM 				 */
1447961SNatalie.Li@Sun.COM 				mutex_enter(&nfscmd_lock);
1457961SNatalie.Li@Sun.COM 				if (dh == nfscmd_dh)
1467961SNatalie.Li@Sun.COM 					nfscmd_dh = NULL;
1477961SNatalie.Li@Sun.COM 				mutex_exit(&nfscmd_lock);
1487961SNatalie.Li@Sun.COM 				door_ki_rele(dh);
1497961SNatalie.Li@Sun.COM 				delay(hz);
1507961SNatalie.Li@Sun.COM 				goto retry;
1517961SNatalie.Li@Sun.COM 			}
1527961SNatalie.Li@Sun.COM 			/*
1537961SNatalie.Li@Sun.COM 			 * If the door was _not_ revoked on us,
1547961SNatalie.Li@Sun.COM 			 * then more than likely we took an INTR,
1557961SNatalie.Li@Sun.COM 			 * so we need to fail the operation.
1567961SNatalie.Li@Sun.COM 			 */
1577961SNatalie.Li@Sun.COM 			door_ki_rele(dh);
1587961SNatalie.Li@Sun.COM 		}
1597961SNatalie.Li@Sun.COM 		/*
1607961SNatalie.Li@Sun.COM 		 * The only failure that can occur from getting
1617961SNatalie.Li@Sun.COM 		 * the door info is EINVAL, so we let the code
1627961SNatalie.Li@Sun.COM 		 * below handle it.
1637961SNatalie.Li@Sun.COM 		 */
1647961SNatalie.Li@Sun.COM 		/* FALLTHROUGH */
1657961SNatalie.Li@Sun.COM 
1667961SNatalie.Li@Sun.COM 	case EBADF:
1677961SNatalie.Li@Sun.COM 	case EINVAL:
1687961SNatalie.Li@Sun.COM 	default:
1697961SNatalie.Li@Sun.COM 		/*
1707961SNatalie.Li@Sun.COM 		 * If we have a stale door handle, give smf a last
1717961SNatalie.Li@Sun.COM 		 * chance to start it by sleeping for a little bit.
1727961SNatalie.Li@Sun.COM 		 * If we're still hosed, we'll fail the call.
1737961SNatalie.Li@Sun.COM 		 *
1747961SNatalie.Li@Sun.COM 		 * Since we're going to reacquire the door handle
1757961SNatalie.Li@Sun.COM 		 * upon the retry, we opt to sleep for a bit and
1767961SNatalie.Li@Sun.COM 		 * _not_ to clear mountd_dh. If mountd restarted
1777961SNatalie.Li@Sun.COM 		 * and was able to set mountd_dh, we should see
1787961SNatalie.Li@Sun.COM 		 * the new instance; if not, we won't get caught
1797961SNatalie.Li@Sun.COM 		 * up in the retry/DELAY loop.
1807961SNatalie.Li@Sun.COM 		 */
1817961SNatalie.Li@Sun.COM 		door_ki_rele(dh);
1827961SNatalie.Li@Sun.COM 		if (!last) {
1837961SNatalie.Li@Sun.COM 			delay(hz);
1847961SNatalie.Li@Sun.COM 			last++;
1857961SNatalie.Li@Sun.COM 			goto retry;
1867961SNatalie.Li@Sun.COM 		}
1877961SNatalie.Li@Sun.COM 		res->error = NFSCMD_ERR_FAIL;
1887961SNatalie.Li@Sun.COM 		break;
1897961SNatalie.Li@Sun.COM 	}
190*11632SJan.Kryl@Sun.COM 	return (res->error);
1917961SNatalie.Li@Sun.COM }
1927961SNatalie.Li@Sun.COM 
1937961SNatalie.Li@Sun.COM /*
1947961SNatalie.Li@Sun.COM  * nfscmd_findmap(export, addr)
1957961SNatalie.Li@Sun.COM  *
196*11632SJan.Kryl@Sun.COM  * Find a characterset map for the specified client address.
197*11632SJan.Kryl@Sun.COM  * First try to find a cached entry. If not successful,
198*11632SJan.Kryl@Sun.COM  * ask mountd daemon running in userland.
199*11632SJan.Kryl@Sun.COM  *
200*11632SJan.Kryl@Sun.COM  * For most of the clients this function is NOOP, since
201*11632SJan.Kryl@Sun.COM  * EX_CHARMAP flag won't be set.
2027961SNatalie.Li@Sun.COM  */
2037961SNatalie.Li@Sun.COM struct charset_cache *
nfscmd_findmap(struct exportinfo * exi,struct sockaddr * sp)2047961SNatalie.Li@Sun.COM nfscmd_findmap(struct exportinfo *exi, struct sockaddr *sp)
2057961SNatalie.Li@Sun.COM {
2067961SNatalie.Li@Sun.COM 	struct charset_cache *charset;
2077961SNatalie.Li@Sun.COM 
208*11632SJan.Kryl@Sun.COM 	/*
209*11632SJan.Kryl@Sun.COM 	 * In debug kernel we want to know about strayed nulls.
210*11632SJan.Kryl@Sun.COM 	 * In non-debug kernel we behave gracefully.
211*11632SJan.Kryl@Sun.COM 	 */
212*11632SJan.Kryl@Sun.COM 	ASSERT(exi != NULL);
213*11632SJan.Kryl@Sun.COM 	ASSERT(sp != NULL);
214*11632SJan.Kryl@Sun.COM 
215*11632SJan.Kryl@Sun.COM 	if (exi == NULL || sp == NULL)
216*11632SJan.Kryl@Sun.COM 		return (NULL);
217*11632SJan.Kryl@Sun.COM 
2187961SNatalie.Li@Sun.COM 	mutex_enter(&exi->exi_lock);
219*11632SJan.Kryl@Sun.COM 
220*11632SJan.Kryl@Sun.COM 	if (!(exi->exi_export.ex_flags & EX_CHARMAP)) {
221*11632SJan.Kryl@Sun.COM 		mutex_exit(&exi->exi_lock);
222*11632SJan.Kryl@Sun.COM 		return (NULL);
223*11632SJan.Kryl@Sun.COM 	}
224*11632SJan.Kryl@Sun.COM 
2257961SNatalie.Li@Sun.COM 	for (charset = exi->exi_charset;
2267961SNatalie.Li@Sun.COM 	    charset != NULL;
2277961SNatalie.Li@Sun.COM 	    charset = charset->next) {
2287961SNatalie.Li@Sun.COM 		if (bcmp(sp, &charset->client_addr,
2297961SNatalie.Li@Sun.COM 		    sizeof (struct sockaddr)) == 0)
2307961SNatalie.Li@Sun.COM 			break;
2317961SNatalie.Li@Sun.COM 	}
2327961SNatalie.Li@Sun.COM 	mutex_exit(&exi->exi_lock);
233*11632SJan.Kryl@Sun.COM 
234*11632SJan.Kryl@Sun.COM 	/* the slooow way - ask daemon */
235*11632SJan.Kryl@Sun.COM 	if (charset == NULL)
236*11632SJan.Kryl@Sun.COM 		charset = nfscmd_charmap(exi, sp);
237*11632SJan.Kryl@Sun.COM 
2387961SNatalie.Li@Sun.COM 	return (charset);
2397961SNatalie.Li@Sun.COM }
2407961SNatalie.Li@Sun.COM 
2417961SNatalie.Li@Sun.COM /*
2427961SNatalie.Li@Sun.COM  * nfscmd_insert_charmap(export, addr, name)
2437961SNatalie.Li@Sun.COM  *
2447961SNatalie.Li@Sun.COM  * Insert a new character set conversion map into the export structure
2457961SNatalie.Li@Sun.COM  * for the share. The entry has the IP address of the client and the
2467961SNatalie.Li@Sun.COM  * character set name.
2477961SNatalie.Li@Sun.COM  */
2487961SNatalie.Li@Sun.COM 
2497961SNatalie.Li@Sun.COM static struct charset_cache *
nfscmd_insert_charmap(struct exportinfo * exi,struct sockaddr * sp,char * name)2507961SNatalie.Li@Sun.COM nfscmd_insert_charmap(struct exportinfo *exi, struct sockaddr *sp, char *name)
2517961SNatalie.Li@Sun.COM {
2527961SNatalie.Li@Sun.COM 	struct charset_cache *charset;
2537961SNatalie.Li@Sun.COM 
2547961SNatalie.Li@Sun.COM 	charset = (struct charset_cache *)
2557961SNatalie.Li@Sun.COM 	    kmem_zalloc(sizeof (struct charset_cache), KM_SLEEP);
2567961SNatalie.Li@Sun.COM 
2577961SNatalie.Li@Sun.COM 	if (charset == NULL)
2587961SNatalie.Li@Sun.COM 		return (NULL);
2597961SNatalie.Li@Sun.COM 	if (name != NULL) {
2607961SNatalie.Li@Sun.COM 		charset->inbound = kiconv_open("UTF-8", name);
2617961SNatalie.Li@Sun.COM 		charset->outbound = kiconv_open(name, "UTF-8");
2627961SNatalie.Li@Sun.COM 	}
2637961SNatalie.Li@Sun.COM 	charset->client_addr = *sp;
2647961SNatalie.Li@Sun.COM 	mutex_enter(&exi->exi_lock);
2657961SNatalie.Li@Sun.COM 	charset->next = exi->exi_charset;
2667961SNatalie.Li@Sun.COM 	exi->exi_charset = charset;
2677961SNatalie.Li@Sun.COM 	mutex_exit(&exi->exi_lock);
2687961SNatalie.Li@Sun.COM 
2697961SNatalie.Li@Sun.COM 	return (charset);
2707961SNatalie.Li@Sun.COM }
2717961SNatalie.Li@Sun.COM 
2727961SNatalie.Li@Sun.COM /*
2737961SNatalie.Li@Sun.COM  * nfscmd_charmap(response, sp, exi)
2747961SNatalie.Li@Sun.COM  *
275*11632SJan.Kryl@Sun.COM  * Check to see if this client needs a character set conversion.
2767961SNatalie.Li@Sun.COM  */
277*11632SJan.Kryl@Sun.COM static struct charset_cache *
nfscmd_charmap(exportinfo_t * exi,struct sockaddr * sp)278*11632SJan.Kryl@Sun.COM nfscmd_charmap(exportinfo_t *exi, struct sockaddr *sp)
2797961SNatalie.Li@Sun.COM {
2807961SNatalie.Li@Sun.COM 	nfscmd_arg_t req;
281*11632SJan.Kryl@Sun.COM 	int ret;
282*11632SJan.Kryl@Sun.COM 	char *path;
2837961SNatalie.Li@Sun.COM 	nfscmd_res_t res;
284*11632SJan.Kryl@Sun.COM 	struct charset_cache *charset;
2857961SNatalie.Li@Sun.COM 
286*11632SJan.Kryl@Sun.COM 	path = exi->exi_export.ex_path;
287*11632SJan.Kryl@Sun.COM 	if (path == NULL)
288*11632SJan.Kryl@Sun.COM 		return (NULL);
2897961SNatalie.Li@Sun.COM 
290*11632SJan.Kryl@Sun.COM 	/*
291*11632SJan.Kryl@Sun.COM 	 * nfscmd_findmap() did not find one in the cache so make
292*11632SJan.Kryl@Sun.COM 	 * the request to the daemon. We need to add the entry in
293*11632SJan.Kryl@Sun.COM 	 * either case since we want negative as well as
294*11632SJan.Kryl@Sun.COM 	 * positive cacheing.
295*11632SJan.Kryl@Sun.COM 	 */
296*11632SJan.Kryl@Sun.COM 	req.cmd = NFSCMD_CHARMAP_LOOKUP;
297*11632SJan.Kryl@Sun.COM 	req.version = NFSCMD_VERSION;
298*11632SJan.Kryl@Sun.COM 	req.arg.charmap.addr = *sp;
299*11632SJan.Kryl@Sun.COM 	(void) strncpy(req.arg.charmap.path, path, MAXPATHLEN);
300*11632SJan.Kryl@Sun.COM 	bzero((caddr_t)&res, sizeof (nfscmd_res_t));
301*11632SJan.Kryl@Sun.COM 	ret = nfscmd_send(&req, &res);
302*11632SJan.Kryl@Sun.COM 	if (ret == NFSCMD_ERR_SUCCESS)
303*11632SJan.Kryl@Sun.COM 		charset = nfscmd_insert_charmap(exi, sp,
304*11632SJan.Kryl@Sun.COM 		    res.result.charmap.codeset);
305*11632SJan.Kryl@Sun.COM 	else
306*11632SJan.Kryl@Sun.COM 		charset = nfscmd_insert_charmap(exi, sp, NULL);
307*11632SJan.Kryl@Sun.COM 
308*11632SJan.Kryl@Sun.COM 	return (charset);
3097961SNatalie.Li@Sun.COM }
3107961SNatalie.Li@Sun.COM 
3117961SNatalie.Li@Sun.COM /*
3127961SNatalie.Li@Sun.COM  * nfscmd_convname(addr, export, name, inbound, size)
3137961SNatalie.Li@Sun.COM  *
3147961SNatalie.Li@Sun.COM  * Convert the given "name" string to the appropriate character set.
3157961SNatalie.Li@Sun.COM  * If inbound is true, convert from the client character set to UTF-8.
3167961SNatalie.Li@Sun.COM  * If inbound is false, convert from UTF-8 to the client characters set.
317*11632SJan.Kryl@Sun.COM  *
318*11632SJan.Kryl@Sun.COM  * In case of NFS v4 this is used for ill behaved clients, since
319*11632SJan.Kryl@Sun.COM  * according to the standard all file names should be utf-8 encoded
320*11632SJan.Kryl@Sun.COM  * on client-side.
3217961SNatalie.Li@Sun.COM  */
3227961SNatalie.Li@Sun.COM 
3237961SNatalie.Li@Sun.COM char *
nfscmd_convname(struct sockaddr * ca,struct exportinfo * exi,char * name,int inbound,size_t size)3247961SNatalie.Li@Sun.COM nfscmd_convname(struct sockaddr *ca, struct exportinfo *exi, char *name,
3257961SNatalie.Li@Sun.COM     int inbound, size_t size)
3267961SNatalie.Li@Sun.COM {
3277961SNatalie.Li@Sun.COM 	char *newname;
3287961SNatalie.Li@Sun.COM 	char *holdname;
3297961SNatalie.Li@Sun.COM 	int err;
3307961SNatalie.Li@Sun.COM 	int ret;
3317961SNatalie.Li@Sun.COM 	size_t nsize;
3327961SNatalie.Li@Sun.COM 	size_t osize;
3337961SNatalie.Li@Sun.COM 	struct charset_cache *charset = NULL;
3347961SNatalie.Li@Sun.COM 
3357961SNatalie.Li@Sun.COM 	charset = nfscmd_findmap(exi, ca);
336*11632SJan.Kryl@Sun.COM 	if (charset == NULL ||
337*11632SJan.Kryl@Sun.COM 	    (charset->inbound == NULL && inbound) ||
338*11632SJan.Kryl@Sun.COM 	    (charset->outbound == NULL && !inbound))
3397961SNatalie.Li@Sun.COM 		return (name);
3407961SNatalie.Li@Sun.COM 
3417961SNatalie.Li@Sun.COM 	/* make sure we have more than enough space */
3427961SNatalie.Li@Sun.COM 	newname = kmem_zalloc(size, KM_SLEEP);
3437961SNatalie.Li@Sun.COM 	nsize = strlen(name);
3447961SNatalie.Li@Sun.COM 	osize = size;
3457961SNatalie.Li@Sun.COM 	holdname = newname;
3467961SNatalie.Li@Sun.COM 	if (inbound)
3477961SNatalie.Li@Sun.COM 		ret = kiconv(charset->inbound, &name, &nsize,
3487961SNatalie.Li@Sun.COM 		    &holdname, &osize, &err);
3497961SNatalie.Li@Sun.COM 	else
3507961SNatalie.Li@Sun.COM 		ret = kiconv(charset->outbound, &name, &nsize,
3517961SNatalie.Li@Sun.COM 		    &holdname, &osize, &err);
3527961SNatalie.Li@Sun.COM 	if (ret == (size_t)-1) {
3537961SNatalie.Li@Sun.COM 		kmem_free(newname, size);
3547961SNatalie.Li@Sun.COM 		newname = NULL;
3557961SNatalie.Li@Sun.COM 	}
3567961SNatalie.Li@Sun.COM 
3577961SNatalie.Li@Sun.COM 	return (newname);
3587961SNatalie.Li@Sun.COM }
3597961SNatalie.Li@Sun.COM 
3607961SNatalie.Li@Sun.COM /*
3617961SNatalie.Li@Sun.COM  * nfscmd_convdirent()
3627961SNatalie.Li@Sun.COM  *
3637961SNatalie.Li@Sun.COM  * There is only one entry in the data.  Convert to new charset, if
3647961SNatalie.Li@Sun.COM  * required and only return a success if it fits.
3657961SNatalie.Li@Sun.COM  */
3667961SNatalie.Li@Sun.COM char *
nfscmd_convdirent(struct sockaddr * ca,struct exportinfo * exi,char * data,size_t size,enum nfsstat3 * error)3677961SNatalie.Li@Sun.COM nfscmd_convdirent(struct sockaddr *ca, struct exportinfo *exi, char *data,
3687961SNatalie.Li@Sun.COM     size_t size, enum nfsstat3 *error)
3697961SNatalie.Li@Sun.COM {
3707961SNatalie.Li@Sun.COM 	char *newdata;
3717961SNatalie.Li@Sun.COM 	size_t ret;
3727961SNatalie.Li@Sun.COM 	size_t nsize;
3737961SNatalie.Li@Sun.COM 	size_t count;
3747961SNatalie.Li@Sun.COM 	int err = 0;
3757961SNatalie.Li@Sun.COM 	char *iname;
3767961SNatalie.Li@Sun.COM 	char *oname;
3777961SNatalie.Li@Sun.COM 	struct charset_cache *charset;
3787961SNatalie.Li@Sun.COM 
3797961SNatalie.Li@Sun.COM 	charset = nfscmd_findmap(exi, ca);
3807961SNatalie.Li@Sun.COM 	if (charset == NULL || charset->outbound == (void *)~0)
3817961SNatalie.Li@Sun.COM 		return (data);
3827961SNatalie.Li@Sun.COM 
3837961SNatalie.Li@Sun.COM 	newdata = kmem_zalloc(size, KM_SLEEP);
3847961SNatalie.Li@Sun.COM 
3857961SNatalie.Li@Sun.COM 	nsize = strlen(((struct dirent64 *)data)->d_name);
3867961SNatalie.Li@Sun.COM 	count = size;
3877961SNatalie.Li@Sun.COM 	bcopy(data, newdata, sizeof (struct dirent64));
3887961SNatalie.Li@Sun.COM 
3897961SNatalie.Li@Sun.COM 	iname = ((struct dirent64 *)data)->d_name;
3907961SNatalie.Li@Sun.COM 	oname = ((struct dirent64 *)newdata)->d_name;
3917961SNatalie.Li@Sun.COM 
3927961SNatalie.Li@Sun.COM 	ret = kiconv(charset->outbound, &iname, &nsize, &oname, &count, &err);
3937961SNatalie.Li@Sun.COM 	if (ret == (size_t)-1) {
3947961SNatalie.Li@Sun.COM 		kmem_free(newdata, size);
3957961SNatalie.Li@Sun.COM 		newdata = NULL;
3967961SNatalie.Li@Sun.COM 		if (err == E2BIG) {
3977961SNatalie.Li@Sun.COM 			if (error != NULL)
3987961SNatalie.Li@Sun.COM 				*error = NFS3ERR_NAMETOOLONG;
3997961SNatalie.Li@Sun.COM 		} else {
4007961SNatalie.Li@Sun.COM 			newdata = data;
4017961SNatalie.Li@Sun.COM 		}
4027961SNatalie.Li@Sun.COM 	} else {
4037961SNatalie.Li@Sun.COM 		ret = strlen(((struct dirent64 *)newdata)->d_name);
4047961SNatalie.Li@Sun.COM 		((struct dirent64 *)newdata)->d_reclen =
4057961SNatalie.Li@Sun.COM 		    DIRENT64_RECLEN(ret + 1);
4067961SNatalie.Li@Sun.COM 	}
4077961SNatalie.Li@Sun.COM 	return (newdata);
4087961SNatalie.Li@Sun.COM }
4097961SNatalie.Li@Sun.COM 
4107961SNatalie.Li@Sun.COM /*
4117961SNatalie.Li@Sun.COM  * nfscmd_convdirplus(addr, export, data, nents, maxsize, ndata)
4127961SNatalie.Li@Sun.COM  *
4137961SNatalie.Li@Sun.COM  * Convert the dirents in data into a new list of dirents in ndata.
4147961SNatalie.Li@Sun.COM  */
4157961SNatalie.Li@Sun.COM 
4167961SNatalie.Li@Sun.COM size_t
nfscmd_convdirplus(struct sockaddr * ca,struct exportinfo * exi,char * data,size_t nents,size_t maxsize,char ** ndata)4177961SNatalie.Li@Sun.COM nfscmd_convdirplus(struct sockaddr *ca, struct exportinfo *exi, char *data,
4187961SNatalie.Li@Sun.COM     size_t nents, size_t maxsize, char **ndata)
4197961SNatalie.Li@Sun.COM {
4207961SNatalie.Li@Sun.COM 	char *newdata;
4217961SNatalie.Li@Sun.COM 	size_t nsize;
4227961SNatalie.Li@Sun.COM 	struct dirent64 *dp;
4237961SNatalie.Li@Sun.COM 	struct dirent64 *ndp;
4247961SNatalie.Li@Sun.COM 	size_t i;
4257961SNatalie.Li@Sun.COM 	size_t ret;
4267961SNatalie.Li@Sun.COM 	char *iname;
4277961SNatalie.Li@Sun.COM 	char *oname;
4287961SNatalie.Li@Sun.COM 	size_t ilen;
4297961SNatalie.Li@Sun.COM 	size_t olen;
4307961SNatalie.Li@Sun.COM 	int err;
4317961SNatalie.Li@Sun.COM 	size_t skipped;
4327961SNatalie.Li@Sun.COM 	struct charset_cache *charset;
4337961SNatalie.Li@Sun.COM 	*ndata = data;	/* return the data if no changes to make */
4347961SNatalie.Li@Sun.COM 
4357961SNatalie.Li@Sun.COM 	charset = nfscmd_findmap(exi, ca);
4367961SNatalie.Li@Sun.COM 
4377961SNatalie.Li@Sun.COM 	if (charset == NULL || charset->outbound == (void *)~0)
4387961SNatalie.Li@Sun.COM 		return (0);
4397961SNatalie.Li@Sun.COM 
4407961SNatalie.Li@Sun.COM 	newdata = kmem_zalloc(maxsize, KM_SLEEP);
4417961SNatalie.Li@Sun.COM 	nsize = 0;
4427961SNatalie.Li@Sun.COM 
4437961SNatalie.Li@Sun.COM 	dp = (struct dirent64 *)data;
4447961SNatalie.Li@Sun.COM 	ndp = (struct dirent64 *)newdata;
4457961SNatalie.Li@Sun.COM 
4467961SNatalie.Li@Sun.COM 	for (skipped = 0, i = 0; i < nents; i++) {
4477961SNatalie.Li@Sun.COM 		/*
4487961SNatalie.Li@Sun.COM 		 * Copy the dp information if it fits. Then copy and
4497961SNatalie.Li@Sun.COM 		 * convert the name in the entry.
4507961SNatalie.Li@Sun.COM 		 */
4517961SNatalie.Li@Sun.COM 		if ((maxsize - nsize) < dp->d_reclen)
4527961SNatalie.Li@Sun.COM 			/* doesn't fit */
4537961SNatalie.Li@Sun.COM 			break;
4547961SNatalie.Li@Sun.COM 		*ndp = *dp;
4557961SNatalie.Li@Sun.COM 		iname = dp->d_name;
4567961SNatalie.Li@Sun.COM 		ilen = strlen(iname);
4577961SNatalie.Li@Sun.COM 		oname = ndp->d_name;
4587961SNatalie.Li@Sun.COM 		olen = MIN(MAXNAMELEN, maxsize - nsize);
4597961SNatalie.Li@Sun.COM 		ret = kiconv(charset->outbound, &iname, &ilen, &oname,
4607961SNatalie.Li@Sun.COM 		    &olen, &err);
4617961SNatalie.Li@Sun.COM 
4627961SNatalie.Li@Sun.COM 		if (ret == (size_t)-1) {
4637961SNatalie.Li@Sun.COM 			switch (err) {
4647961SNatalie.Li@Sun.COM 			default:
4657961SNatalie.Li@Sun.COM 			case E2BIG:
4667961SNatalie.Li@Sun.COM 				break;
4677961SNatalie.Li@Sun.COM 			case EILSEQ:
4687961SNatalie.Li@Sun.COM 				skipped++;
4697961SNatalie.Li@Sun.COM 				dp = nextdp(dp);
4707961SNatalie.Li@Sun.COM 				continue;
4717961SNatalie.Li@Sun.COM 			}
4727961SNatalie.Li@Sun.COM 		}
4737961SNatalie.Li@Sun.COM 		ilen = MIN(MAXNAMELEN, maxsize - nsize) - olen;
4747961SNatalie.Li@Sun.COM 		ndp->d_name[ilen] = '\0';
4757961SNatalie.Li@Sun.COM 		/*
4767961SNatalie.Li@Sun.COM 		 * What to do with other errors?
4777961SNatalie.Li@Sun.COM 		 * For now, we return the unconverted string.
4787961SNatalie.Li@Sun.COM 		 */
4797961SNatalie.Li@Sun.COM 		ndp->d_reclen = DIRENT64_RECLEN(strlen(ndp->d_name) + 1);
4807961SNatalie.Li@Sun.COM 		nsize += ndp->d_reclen;
4817961SNatalie.Li@Sun.COM 		dp = nextdp(dp);
4827961SNatalie.Li@Sun.COM 		ndp = nextdp(ndp);
4837961SNatalie.Li@Sun.COM 	}
4847961SNatalie.Li@Sun.COM 
4857961SNatalie.Li@Sun.COM 	*ndata = newdata;
4867961SNatalie.Li@Sun.COM 	return (nents - (i + skipped));
4877961SNatalie.Li@Sun.COM }
4887961SNatalie.Li@Sun.COM 
4897961SNatalie.Li@Sun.COM /*
4907961SNatalie.Li@Sun.COM  * nfscmd_countents(data, len)
4917961SNatalie.Li@Sun.COM  *
4927961SNatalie.Li@Sun.COM  * How many dirents are there in the data buffer?
4937961SNatalie.Li@Sun.COM  */
4947961SNatalie.Li@Sun.COM 
4957961SNatalie.Li@Sun.COM size_t
nfscmd_countents(char * data,size_t len)4967961SNatalie.Li@Sun.COM nfscmd_countents(char *data, size_t len)
4977961SNatalie.Li@Sun.COM {
4987961SNatalie.Li@Sun.COM 	struct dirent64 *dp = (struct dirent64 *)data;
4997961SNatalie.Li@Sun.COM 	size_t curlen;
5007961SNatalie.Li@Sun.COM 	size_t reclen;
5017961SNatalie.Li@Sun.COM 	size_t nents;
5027961SNatalie.Li@Sun.COM 
5037961SNatalie.Li@Sun.COM 	for (nents = 0, curlen = 0; curlen < len; curlen += reclen, nents++) {
5047961SNatalie.Li@Sun.COM 		reclen = dp->d_reclen;
5057961SNatalie.Li@Sun.COM 		dp = nextdp(dp);
5067961SNatalie.Li@Sun.COM 	}
5077961SNatalie.Li@Sun.COM 	return (nents);
5087961SNatalie.Li@Sun.COM }
5097961SNatalie.Li@Sun.COM 
5107961SNatalie.Li@Sun.COM /*
5117961SNatalie.Li@Sun.COM  * nfscmd_dropped_entrysize(dir, drop, nents)
5127961SNatalie.Li@Sun.COM  *
5137961SNatalie.Li@Sun.COM  * We need to drop "drop" entries from dir in order to fit in the
5147961SNatalie.Li@Sun.COM  * buffer.  How much do we reduce the overall size by?
5157961SNatalie.Li@Sun.COM  */
5167961SNatalie.Li@Sun.COM 
5177961SNatalie.Li@Sun.COM size_t
nfscmd_dropped_entrysize(struct dirent64 * dir,size_t drop,size_t nents)5187961SNatalie.Li@Sun.COM nfscmd_dropped_entrysize(struct dirent64 *dir, size_t drop, size_t nents)
5197961SNatalie.Li@Sun.COM {
5207961SNatalie.Li@Sun.COM 	size_t size;
5217961SNatalie.Li@Sun.COM 	size_t i;
5227961SNatalie.Li@Sun.COM 
5237961SNatalie.Li@Sun.COM 	for (i = nents - drop; i > 0 && dir != NULL; i--)
5247961SNatalie.Li@Sun.COM 		dir = nextdp(dir);
5257961SNatalie.Li@Sun.COM 
5267961SNatalie.Li@Sun.COM 	if (dir == NULL)
5277961SNatalie.Li@Sun.COM 		return (0);
5287961SNatalie.Li@Sun.COM 
5297961SNatalie.Li@Sun.COM 	for (size = 0, i = 0; i < drop && dir != NULL; i++) {
5307961SNatalie.Li@Sun.COM 		size += dir->d_reclen;
5317961SNatalie.Li@Sun.COM 		dir = nextdp(dir);
5327961SNatalie.Li@Sun.COM 	}
5337961SNatalie.Li@Sun.COM 	return (size);
5347961SNatalie.Li@Sun.COM }
535