xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_door.c (revision 85dff7a0)
1d62bc4baSyz /*
2d62bc4baSyz  * CDDL HEADER START
3d62bc4baSyz  *
4d62bc4baSyz  * The contents of this file are subject to the terms of the
5d62bc4baSyz  * Common Development and Distribution License (the "License").
6d62bc4baSyz  * You may not use this file except in compliance with the License.
7d62bc4baSyz  *
8d62bc4baSyz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz  * See the License for the specific language governing permissions
11d62bc4baSyz  * and limitations under the License.
12d62bc4baSyz  *
13d62bc4baSyz  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz  *
19d62bc4baSyz  * CDDL HEADER END
20d62bc4baSyz  */
21d62bc4baSyz 
22d62bc4baSyz /*
2332715170SCathy Zhou  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24*85dff7a0SAndy Fiddaman  * Copyright 2016 Joyent, Inc.
25*85dff7a0SAndy Fiddaman  * Copyright 2023 Oxide Computer Company
26d62bc4baSyz  */
27d62bc4baSyz 
28d62bc4baSyz /*
29d62bc4baSyz  * Main door handler functions used by dlmgmtd to process the different door
30d62bc4baSyz  * call requests. Door call requests can come from the user-land applications,
31024b0a25Sseb  * or from the kernel.
322b24ab6bSSebastien Roy  *
332b24ab6bSSebastien Roy  * Note on zones handling:
342b24ab6bSSebastien Roy  *
352b24ab6bSSebastien Roy  * There are two zoneid's associated with a link.  One is the zoneid of the
362b24ab6bSSebastien Roy  * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and
372b24ab6bSSebastien Roy  * the other is the zoneid of the zone where the link is currently assigned
382b24ab6bSSebastien Roy  * (the "zone" link property).  The two can be different if a datalink is
392b24ab6bSSebastien Roy  * created in the global zone and subsequently assigned to a non-global zone
402b24ab6bSSebastien Roy  * via zonecfg or via explicitly setting the "zone" link property.
412b24ab6bSSebastien Roy  *
422b24ab6bSSebastien Roy  * Door clients can see links that were created in their zone, and links that
432b24ab6bSSebastien Roy  * are currently assigned to their zone.  Door clients in a zone can only
442b24ab6bSSebastien Roy  * modify links that were created in their zone.
452b24ab6bSSebastien Roy  *
462b24ab6bSSebastien Roy  * The datalink ID space is global, while each zone has its own datalink name
472b24ab6bSSebastien Roy  * space.  This allows each zone to have complete freedom over the names that
482b24ab6bSSebastien Roy  * they assign to links created within the zone.
49d62bc4baSyz  */
50d62bc4baSyz 
51d62bc4baSyz #include <assert.h>
52d62bc4baSyz #include <alloca.h>
53024b0a25Sseb #include <errno.h>
54024b0a25Sseb #include <priv_utils.h>
55024b0a25Sseb #include <stdlib.h>
56d62bc4baSyz #include <strings.h>
570dc974a9SCathy Zhou #include <syslog.h>
580dc974a9SCathy Zhou #include <sys/sysevent/eventdefs.h>
592b24ab6bSSebastien Roy #include <zone.h>
600dc974a9SCathy Zhou #include <libsysevent.h>
61d62bc4baSyz #include <libdlmgmt.h>
620dc974a9SCathy Zhou #include <librcm.h>
63d62bc4baSyz #include "dlmgmt_impl.h"
64d62bc4baSyz 
6532715170SCathy Zhou typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
6632715170SCathy Zhou     ucred_t *);
67024b0a25Sseb 
68024b0a25Sseb typedef struct dlmgmt_door_info_s {
69024b0a25Sseb 	uint_t			di_cmd;
70024b0a25Sseb 	size_t			di_reqsz;
71024b0a25Sseb 	size_t			di_acksz;
72024b0a25Sseb 	dlmgmt_door_handler_t	*di_handler;
73024b0a25Sseb } dlmgmt_door_info_t;
74024b0a25Sseb 
752b24ab6bSSebastien Roy /*
762b24ab6bSSebastien Roy  * Check if the caller has the required privileges to operate on a link of the
772b24ab6bSSebastien Roy  * given class.
782b24ab6bSSebastien Roy  */
792b24ab6bSSebastien Roy static int
dlmgmt_checkprivs(datalink_class_t class,ucred_t * cred)802b24ab6bSSebastien Roy dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
812b24ab6bSSebastien Roy {
822b24ab6bSSebastien Roy 	const priv_set_t *eset;
832b24ab6bSSebastien Roy 
842b24ab6bSSebastien Roy 	eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
852b24ab6bSSebastien Roy 	if (eset != NULL && ((class == DATALINK_CLASS_IPTUN &&
862b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_IPTUN_CONFIG)) ||
872b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_DL_CONFIG) ||
882b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_NET_CONFIG)))
892b24ab6bSSebastien Roy 		return (0);
902b24ab6bSSebastien Roy 	return (EACCES);
912b24ab6bSSebastien Roy }
92024b0a25Sseb 
93d62bc4baSyz static dlmgmt_link_t *
dlmgmt_getlink_by_dev(char * devname,zoneid_t zoneid)942b24ab6bSSebastien Roy dlmgmt_getlink_by_dev(char *devname, zoneid_t zoneid)
95d62bc4baSyz {
96d62bc4baSyz 	dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl);
97d62bc4baSyz 
98d62bc4baSyz 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
992b24ab6bSSebastien Roy 		if (link_is_visible(linkp, zoneid) &&
1002b24ab6bSSebastien Roy 		    (linkp->ll_class == DATALINK_CLASS_PHYS) &&
101d62bc4baSyz 		    linkattr_equal(&(linkp->ll_head), FDEVNAME, devname,
102d62bc4baSyz 		    strlen(devname) + 1)) {
103d62bc4baSyz 			return (linkp);
104d62bc4baSyz 		}
105d62bc4baSyz 	}
106d62bc4baSyz 	return (NULL);
107d62bc4baSyz }
108d62bc4baSyz 
1090dc974a9SCathy Zhou /*
1100dc974a9SCathy Zhou  * Post the EC_DATALINK sysevent for the given linkid. This sysevent will
1110dc974a9SCathy Zhou  * be consumed by the datalink sysevent module.
1120dc974a9SCathy Zhou  */
1130dc974a9SCathy Zhou static void
dlmgmt_post_sysevent(const char * subclass,datalink_id_t linkid,boolean_t reconfigured)1145093e103SCathy Zhou dlmgmt_post_sysevent(const char *subclass, datalink_id_t linkid,
1155093e103SCathy Zhou     boolean_t reconfigured)
1160dc974a9SCathy Zhou {
1170dc974a9SCathy Zhou 	nvlist_t	*nvl = NULL;
1180dc974a9SCathy Zhou 	sysevent_id_t	eid;
1190dc974a9SCathy Zhou 	int		err;
1200dc974a9SCathy Zhou 
1210dc974a9SCathy Zhou 	if (((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) ||
1225093e103SCathy Zhou 	    ((err = nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid)) != 0) ||
1235093e103SCathy Zhou 	    ((err = nvlist_add_boolean_value(nvl, RCM_NV_RECONFIGURED,
1245093e103SCathy Zhou 	    reconfigured)) != 0)) {
1250dc974a9SCathy Zhou 		goto done;
1260dc974a9SCathy Zhou 	}
1270dc974a9SCathy Zhou 
1280dc974a9SCathy Zhou 	if (sysevent_post_event(EC_DATALINK, (char *)subclass, SUNW_VENDOR,
1290dc974a9SCathy Zhou 	    (char *)progname, nvl, &eid) == -1) {
1300dc974a9SCathy Zhou 		err = errno;
1310dc974a9SCathy Zhou 	}
1320dc974a9SCathy Zhou 
1330dc974a9SCathy Zhou done:
1340dc974a9SCathy Zhou 	if (err != 0) {
1350dc974a9SCathy Zhou 		dlmgmt_log(LOG_WARNING, "dlmgmt_post_sysevent(%d) failed: %s",
1360dc974a9SCathy Zhou 		    linkid, strerror(err));
1370dc974a9SCathy Zhou 	}
1380dc974a9SCathy Zhou 	nvlist_free(nvl);
1390dc974a9SCathy Zhou }
1400dc974a9SCathy Zhou 
14132715170SCathy Zhou /* ARGSUSED */
142d62bc4baSyz static void
dlmgmt_upcall_create(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)14332715170SCathy Zhou dlmgmt_upcall_create(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
14432715170SCathy Zhou     ucred_t *cred)
145d62bc4baSyz {
146024b0a25Sseb 	dlmgmt_upcall_arg_create_t *create = argp;
147024b0a25Sseb 	dlmgmt_create_retval_t	*retvalp = retp;
148d62bc4baSyz 	datalink_class_t	class;
149d62bc4baSyz 	uint32_t		media;
150d62bc4baSyz 	dlmgmt_link_t		*linkp;
151d62bc4baSyz 	char			link[MAXLINKNAMELEN];
152d62bc4baSyz 	uint32_t		flags;
1538a1c9a22SCathy Zhou 	int			err = 0;
154d62bc4baSyz 	boolean_t		created = B_FALSE;
1555093e103SCathy Zhou 	boolean_t		reconfigured = B_FALSE;
156d62bc4baSyz 
157d62bc4baSyz 	/*
158d62bc4baSyz 	 * Determine whether this link is persistent. Note that this request
159d62bc4baSyz 	 * is coming from kernel so this link must be active.
160d62bc4baSyz 	 */
161d62bc4baSyz 	flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0);
162d62bc4baSyz 
163d62bc4baSyz 	class = create->ld_class;
164d62bc4baSyz 	media = create->ld_media;
165d62bc4baSyz 
166d62bc4baSyz 	/*
167d62bc4baSyz 	 * Hold the writer lock to update the link table.
168d62bc4baSyz 	 */
169d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
170d62bc4baSyz 
1712b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(class, cred)) != 0)
1722b24ab6bSSebastien Roy 		goto done;
1732b24ab6bSSebastien Roy 
174d62bc4baSyz 	/*
175d62bc4baSyz 	 * Check to see whether this is the reattachment of an existing
176d62bc4baSyz 	 * physical link. If so, return its linkid.
177d62bc4baSyz 	 */
1782b24ab6bSSebastien Roy 	if ((class == DATALINK_CLASS_PHYS) && (linkp =
1792b24ab6bSSebastien Roy 	    dlmgmt_getlink_by_dev(create->ld_devname, zoneid)) != NULL) {
1808a1c9a22SCathy Zhou 		if (linkattr_equal(&(linkp->ll_head), FPHYMAJ,
1818a1c9a22SCathy Zhou 		    &create->ld_phymaj, sizeof (uint64_t)) &&
1828a1c9a22SCathy Zhou 		    linkattr_equal(&(linkp->ll_head), FPHYINST,
1838a1c9a22SCathy Zhou 		    &create->ld_phyinst, sizeof (uint64_t)) &&
1848a1c9a22SCathy Zhou 		    (linkp->ll_flags & flags) == flags) {
1858a1c9a22SCathy Zhou 			/*
1868a1c9a22SCathy Zhou 			 * If nothing has been changed, directly return.
1878a1c9a22SCathy Zhou 			 */
1888a1c9a22SCathy Zhou 			goto noupdate;
1898a1c9a22SCathy Zhou 		}
1908a1c9a22SCathy Zhou 
191d62bc4baSyz 		err = linkattr_set(&(linkp->ll_head), FPHYMAJ,
192d62bc4baSyz 		    &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64);
193d62bc4baSyz 		if (err != 0)
194d62bc4baSyz 			goto done;
195d62bc4baSyz 
196d62bc4baSyz 		err = linkattr_set(&(linkp->ll_head), FPHYINST,
197d62bc4baSyz 		    &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64);
198d62bc4baSyz 		if (err != 0)
199d62bc4baSyz 			goto done;
200d62bc4baSyz 
2015093e103SCathy Zhou 		/*
2025093e103SCathy Zhou 		 * This is a device that is dynamic reconfigured.
2035093e103SCathy Zhou 		 */
2045093e103SCathy Zhou 		if ((linkp->ll_flags & DLMGMT_ACTIVE) == 0)
2055093e103SCathy Zhou 			reconfigured = B_TRUE;
2065093e103SCathy Zhou 
2072b24ab6bSSebastien Roy 		if ((err = link_activate(linkp)) != 0)
2082b24ab6bSSebastien Roy 			goto done;
209d62bc4baSyz 		linkp->ll_flags |= flags;
210d62bc4baSyz 		linkp->ll_gen++;
2115093e103SCathy Zhou 
212d62bc4baSyz 		goto done;
213d62bc4baSyz 	}
214d62bc4baSyz 
215d62bc4baSyz 	if ((err = dlmgmt_create_common(create->ld_devname, class, media,
2162b24ab6bSSebastien Roy 	    zoneid, flags, &linkp)) == EEXIST) {
217d62bc4baSyz 		/*
218d62bc4baSyz 		 * The link name already exists. Return error if this is a
219d62bc4baSyz 		 * non-physical link (in that case, the link name must be
220d62bc4baSyz 		 * the same as the given name).
221d62bc4baSyz 		 */
222d62bc4baSyz 		if (class != DATALINK_CLASS_PHYS)
223d62bc4baSyz 			goto done;
224d62bc4baSyz 
225d62bc4baSyz 		/*
226d62bc4baSyz 		 * The physical link's name already exists, request
227d62bc4baSyz 		 * a suggested link name: net<nextppa>
228d62bc4baSyz 		 */
2292b24ab6bSSebastien Roy 		err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN, zoneid);
230d62bc4baSyz 		if (err != 0)
231d62bc4baSyz 			goto done;
232d62bc4baSyz 
2332b24ab6bSSebastien Roy 		err = dlmgmt_create_common(link, class, media, zoneid, flags,
2342b24ab6bSSebastien Roy 		    &linkp);
235d62bc4baSyz 	}
236d62bc4baSyz 
237d62bc4baSyz 	if (err != 0)
238d62bc4baSyz 		goto done;
239d62bc4baSyz 
240d62bc4baSyz 	created = B_TRUE;
241d62bc4baSyz 
242d62bc4baSyz 	/*
243d62bc4baSyz 	 * This is a new link.  Only need to persist link attributes for
244d62bc4baSyz 	 * physical links.
245d62bc4baSyz 	 */
246d62bc4baSyz 	if (class == DATALINK_CLASS_PHYS &&
247d62bc4baSyz 	    (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname,
248d62bc4baSyz 	    strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) ||
249d62bc4baSyz 	    ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj,
250d62bc4baSyz 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) ||
251d62bc4baSyz 	    ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst,
252d62bc4baSyz 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) {
253d62bc4baSyz 		(void) dlmgmt_destroy_common(linkp, flags);
254d62bc4baSyz 	}
255d62bc4baSyz 
256d62bc4baSyz done:
2572b24ab6bSSebastien Roy 	if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_link, linkp,
258d62bc4baSyz 	    linkp->ll_flags)) != 0) && created) {
259d62bc4baSyz 		(void) dlmgmt_destroy_common(linkp, flags);
260d62bc4baSyz 	}
261d62bc4baSyz 
2628a1c9a22SCathy Zhou noupdate:
263d62bc4baSyz 	if (err == 0)
264d62bc4baSyz 		retvalp->lr_linkid = linkp->ll_linkid;
265d62bc4baSyz 
266d62bc4baSyz 	dlmgmt_table_unlock();
2670dc974a9SCathy Zhou 
2688a1c9a22SCathy Zhou 	if ((err == 0) && (class == DATALINK_CLASS_PHYS)) {
2690dc974a9SCathy Zhou 		/*
2700dc974a9SCathy Zhou 		 * Post the ESC_DATALINK_PHYS_ADD sysevent. This sysevent
2710dc974a9SCathy Zhou 		 * is consumed by the datalink sysevent module which in
2720dc974a9SCathy Zhou 		 * turn generates the RCM_RESOURCE_LINK_NEW RCM event.
2730dc974a9SCathy Zhou 		 */
2745093e103SCathy Zhou 		dlmgmt_post_sysevent(ESC_DATALINK_PHYS_ADD,
2755093e103SCathy Zhou 		    retvalp->lr_linkid, reconfigured);
2760dc974a9SCathy Zhou 	}
2770dc974a9SCathy Zhou 
2780dc974a9SCathy Zhou 	retvalp->lr_err = err;
279d62bc4baSyz }
280d62bc4baSyz 
28132715170SCathy Zhou /* ARGSUSED */
282d62bc4baSyz static void
dlmgmt_upcall_update(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)28332715170SCathy Zhou dlmgmt_upcall_update(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
28432715170SCathy Zhou     ucred_t *cred)
285d62bc4baSyz {
286024b0a25Sseb 	dlmgmt_upcall_arg_update_t	*update = argp;
287024b0a25Sseb 	dlmgmt_update_retval_t		*retvalp = retp;
288024b0a25Sseb 	uint32_t			media = update->ld_media;
289024b0a25Sseb 	dlmgmt_link_t			*linkp;
290024b0a25Sseb 	int				err = 0;
291d62bc4baSyz 
292d62bc4baSyz 	/*
293d62bc4baSyz 	 * Hold the writer lock to update the link table.
294d62bc4baSyz 	 */
295d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
296d62bc4baSyz 
297d62bc4baSyz 	/*
298d62bc4baSyz 	 * Check to see whether this is the reattachment of an existing
299d62bc4baSyz 	 * physical link. If so, return its linkid.
300d62bc4baSyz 	 */
3012b24ab6bSSebastien Roy 	if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname, zoneid)) ==
3022b24ab6bSSebastien Roy 	    NULL) {
303d62bc4baSyz 		err = ENOENT;
304d62bc4baSyz 		goto done;
305d62bc4baSyz 	}
306d62bc4baSyz 
3072b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
3082b24ab6bSSebastien Roy 		goto done;
3092b24ab6bSSebastien Roy 
310d62bc4baSyz 	retvalp->lr_linkid = linkp->ll_linkid;
311d62bc4baSyz 	retvalp->lr_media = media;
312d62bc4baSyz 	if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) {
313d62bc4baSyz 		/*
314d62bc4baSyz 		 * Assume a DL_ETHER link ce0, a DL_WIFI link ath0
315d62bc4baSyz 		 * 1. # dladm rename-link ce0 net0
316d62bc4baSyz 		 * 2. DR out ce0. net0 is down.
317d62bc4baSyz 		 * 3. use rename-link to have the ath0 device inherit
318d62bc4baSyz 		 *    the configuration from net0
319d62bc4baSyz 		 *    # dladm rename-link ath0 net0
320d62bc4baSyz 		 * 4. DR in ath0.
321d62bc4baSyz 		 * As ath0 and ce0 do not have the same media type, ath0
322d62bc4baSyz 		 * cannot inherit the configuration of net0.
323d62bc4baSyz 		 */
324d62bc4baSyz 		err = EEXIST;
325d62bc4baSyz 
326d62bc4baSyz 		/*
327d62bc4baSyz 		 * Return the media type of the existing link to indicate the
328d62bc4baSyz 		 * reason for the name conflict.
329d62bc4baSyz 		 */
330d62bc4baSyz 		retvalp->lr_media = linkp->ll_media;
331d62bc4baSyz 		goto done;
332d62bc4baSyz 	}
333d62bc4baSyz 
334d62bc4baSyz 	if (update->ld_novanity &&
335d62bc4baSyz 	    (strcmp(update->ld_devname, linkp->ll_link) != 0)) {
336d62bc4baSyz 		/*
337d62bc4baSyz 		 * Return an error if this is a physical link that does not
338d62bc4baSyz 		 * support vanity naming, but the link name is not the same
339d62bc4baSyz 		 * as the given device name.
340d62bc4baSyz 		 */
341d62bc4baSyz 		err = EEXIST;
342d62bc4baSyz 		goto done;
343d62bc4baSyz 	}
344d62bc4baSyz 
3458a1c9a22SCathy Zhou 	if (linkp->ll_media != media) {
3468a1c9a22SCathy Zhou 		linkp->ll_media = media;
3478a1c9a22SCathy Zhou 		linkp->ll_gen++;
3482b24ab6bSSebastien Roy 		(void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
3492b24ab6bSSebastien Roy 		    linkp->ll_flags);
3508a1c9a22SCathy Zhou 	}
351d62bc4baSyz 
352d62bc4baSyz done:
353d62bc4baSyz 	dlmgmt_table_unlock();
354d62bc4baSyz 	retvalp->lr_err = err;
355d62bc4baSyz }
356d62bc4baSyz 
35732715170SCathy Zhou /* ARGSUSED */
358d62bc4baSyz static void
dlmgmt_upcall_destroy(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)35932715170SCathy Zhou dlmgmt_upcall_destroy(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
36032715170SCathy Zhou     ucred_t *cred)
361d62bc4baSyz {
362024b0a25Sseb 	dlmgmt_upcall_arg_destroy_t	*destroy = argp;
363024b0a25Sseb 	dlmgmt_destroy_retval_t		*retvalp = retp;
364024b0a25Sseb 	datalink_id_t			linkid = destroy->ld_linkid;
365024b0a25Sseb 	dlmgmt_link_t			*linkp = NULL;
366024b0a25Sseb 	uint32_t			flags, dflags = 0;
367024b0a25Sseb 	int				err = 0;
368d62bc4baSyz 
369d62bc4baSyz 	flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
370d62bc4baSyz 
371d62bc4baSyz 	/*
372d62bc4baSyz 	 * Hold the writer lock to update the link table.
373d62bc4baSyz 	 */
374d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
375d62bc4baSyz 
3762b24ab6bSSebastien Roy 	if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
377d62bc4baSyz 		err = ENOENT;
378d62bc4baSyz 		goto done;
379d62bc4baSyz 	}
380d62bc4baSyz 
3812b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
3822b24ab6bSSebastien Roy 		goto done;
3832b24ab6bSSebastien Roy 
384a73e6fc1SCathy Zhou 	if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
3852b24ab6bSSebastien Roy 		if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
386a73e6fc1SCathy Zhou 			goto done;
387a73e6fc1SCathy Zhou 		dflags |= DLMGMT_ACTIVE;
388d62bc4baSyz 	}
389d62bc4baSyz 
390a73e6fc1SCathy Zhou 	if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) {
3912b24ab6bSSebastien Roy 		if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0)
392a73e6fc1SCathy Zhou 			goto done;
393d62bc4baSyz 		dflags |= DLMGMT_PERSIST;
394d62bc4baSyz 	}
395d62bc4baSyz 
396a73e6fc1SCathy Zhou 	err = dlmgmt_destroy_common(linkp, flags);
397a73e6fc1SCathy Zhou done:
398a73e6fc1SCathy Zhou 	if (err != 0 && dflags != 0)
3992b24ab6bSSebastien Roy 		(void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags);
400d62bc4baSyz 
401d62bc4baSyz 	dlmgmt_table_unlock();
402d62bc4baSyz 	retvalp->lr_err = err;
403d62bc4baSyz }
404d62bc4baSyz 
4052b24ab6bSSebastien Roy /* ARGSUSED */
406d62bc4baSyz static void
dlmgmt_getname(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)40732715170SCathy Zhou dlmgmt_getname(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
40832715170SCathy Zhou     ucred_t *cred)
409d62bc4baSyz {
410024b0a25Sseb 	dlmgmt_door_getname_t	*getname = argp;
411024b0a25Sseb 	dlmgmt_getname_retval_t	*retvalp = retp;
412024b0a25Sseb 	dlmgmt_link_t		*linkp;
413024b0a25Sseb 	int			err = 0;
414d62bc4baSyz 
415d62bc4baSyz 	/*
416d62bc4baSyz 	 * Hold the reader lock to access the link
417d62bc4baSyz 	 */
418d62bc4baSyz 	dlmgmt_table_lock(B_FALSE);
4192b24ab6bSSebastien Roy 	if ((linkp = link_by_id(getname->ld_linkid, zoneid)) == NULL) {
420d62bc4baSyz 		err = ENOENT;
4212b24ab6bSSebastien Roy 	} else if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >=
422d62bc4baSyz 	    MAXLINKNAMELEN) {
423d62bc4baSyz 		err = ENOSPC;
4242b24ab6bSSebastien Roy 	} else {
4252b24ab6bSSebastien Roy 		retvalp->lr_flags = linkp->ll_flags;
4262b24ab6bSSebastien Roy 		retvalp->lr_class = linkp->ll_class;
4272b24ab6bSSebastien Roy 		retvalp->lr_media = linkp->ll_media;
428*85dff7a0SAndy Fiddaman 		retvalp->lr_flags |= linkp->ll_transient ? DLMGMT_TRANSIENT : 0;
429d62bc4baSyz 	}
430d62bc4baSyz 
431d62bc4baSyz 	dlmgmt_table_unlock();
432d62bc4baSyz 	retvalp->lr_err = err;
433d62bc4baSyz }
434d62bc4baSyz 
4352b24ab6bSSebastien Roy /* ARGSUSED */
436d62bc4baSyz static void
dlmgmt_getlinkid(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)43732715170SCathy Zhou dlmgmt_getlinkid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
43832715170SCathy Zhou     ucred_t *cred)
439d62bc4baSyz {
440024b0a25Sseb 	dlmgmt_door_getlinkid_t	*getlinkid = argp;
441024b0a25Sseb 	dlmgmt_getlinkid_retval_t *retvalp = retp;
442024b0a25Sseb 	dlmgmt_link_t		*linkp;
443024b0a25Sseb 	int			err = 0;
444d62bc4baSyz 
445d62bc4baSyz 	/*
446d62bc4baSyz 	 * Hold the reader lock to access the link
447d62bc4baSyz 	 */
448d62bc4baSyz 	dlmgmt_table_lock(B_FALSE);
4492b24ab6bSSebastien Roy 
4502b24ab6bSSebastien Roy 	if ((linkp = link_by_name(getlinkid->ld_link, zoneid)) == NULL) {
451d62bc4baSyz 		/*
4522b24ab6bSSebastien Roy 		 * The link does not exist in this zone.
453d62bc4baSyz 		 */
454d62bc4baSyz 		err = ENOENT;
455d62bc4baSyz 		goto done;
456d62bc4baSyz 	}
457d62bc4baSyz 
458d62bc4baSyz 	retvalp->lr_linkid = linkp->ll_linkid;
459d62bc4baSyz 	retvalp->lr_flags = linkp->ll_flags;
460d62bc4baSyz 	retvalp->lr_class = linkp->ll_class;
461d62bc4baSyz 	retvalp->lr_media = linkp->ll_media;
462*85dff7a0SAndy Fiddaman 	retvalp->lr_flags |= linkp->ll_transient ? DLMGMT_TRANSIENT : 0;
463d62bc4baSyz 
464d62bc4baSyz done:
465d62bc4baSyz 	dlmgmt_table_unlock();
466d62bc4baSyz 	retvalp->lr_err = err;
467d62bc4baSyz }
468d62bc4baSyz 
4692b24ab6bSSebastien Roy /* ARGSUSED */
470d62bc4baSyz static void
dlmgmt_getnext(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)47132715170SCathy Zhou dlmgmt_getnext(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
47232715170SCathy Zhou     ucred_t *cred)
473d62bc4baSyz {
474024b0a25Sseb 	dlmgmt_door_getnext_t	*getnext = argp;
475024b0a25Sseb 	dlmgmt_getnext_retval_t	*retvalp = retp;
476024b0a25Sseb 	dlmgmt_link_t		link, *linkp;
477024b0a25Sseb 	avl_index_t		where;
478024b0a25Sseb 	int			err = 0;
479d62bc4baSyz 
480d62bc4baSyz 	/*
481d62bc4baSyz 	 * Hold the reader lock to access the link
482d62bc4baSyz 	 */
483d62bc4baSyz 	dlmgmt_table_lock(B_FALSE);
484d62bc4baSyz 
4852b24ab6bSSebastien Roy 	link.ll_linkid = (getnext->ld_linkid + 1);
4862b24ab6bSSebastien Roy 	if ((linkp = avl_find(&dlmgmt_id_avl, &link, &where)) == NULL)
487d62bc4baSyz 		linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER);
488d62bc4baSyz 
489d62bc4baSyz 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
4902b24ab6bSSebastien Roy 		if (!link_is_visible(linkp, zoneid))
4912b24ab6bSSebastien Roy 			continue;
492d62bc4baSyz 		if ((linkp->ll_class & getnext->ld_class) &&
493d62bc4baSyz 		    (linkp->ll_flags & getnext->ld_flags) &&
494d62bc4baSyz 		    DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia,
495d62bc4baSyz 		    linkp->ll_media))
496d62bc4baSyz 			break;
497d62bc4baSyz 	}
498d62bc4baSyz 
499d62bc4baSyz 	if (linkp == NULL) {
500d62bc4baSyz 		err = ENOENT;
501d62bc4baSyz 	} else {
502d62bc4baSyz 		retvalp->lr_linkid = linkp->ll_linkid;
503d62bc4baSyz 		retvalp->lr_class = linkp->ll_class;
504d62bc4baSyz 		retvalp->lr_media = linkp->ll_media;
505d62bc4baSyz 		retvalp->lr_flags = linkp->ll_flags;
506*85dff7a0SAndy Fiddaman 		retvalp->lr_flags |= linkp->ll_transient ? DLMGMT_TRANSIENT : 0;
507d62bc4baSyz 	}
508d62bc4baSyz 
509d62bc4baSyz 	dlmgmt_table_unlock();
510d62bc4baSyz 	retvalp->lr_err = err;
511d62bc4baSyz }
512d62bc4baSyz 
5132b24ab6bSSebastien Roy /* ARGSUSED */
514024b0a25Sseb static void
dlmgmt_upcall_getattr(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)51532715170SCathy Zhou dlmgmt_upcall_getattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
51632715170SCathy Zhou     ucred_t *cred)
517d62bc4baSyz {
518024b0a25Sseb 	dlmgmt_upcall_arg_getattr_t	*getattr = argp;
519024b0a25Sseb 	dlmgmt_getattr_retval_t		*retvalp = retp;
520024b0a25Sseb 	dlmgmt_link_t			*linkp;
521d62bc4baSyz 
522d62bc4baSyz 	/*
523d62bc4baSyz 	 * Hold the reader lock to access the link
524d62bc4baSyz 	 */
525d62bc4baSyz 	dlmgmt_table_lock(B_FALSE);
5262b24ab6bSSebastien Roy 	if ((linkp = link_by_id(getattr->ld_linkid, zoneid)) == NULL) {
527f1956ffeSCathy Zhou 		retvalp->lr_err = ENOENT;
5282b24ab6bSSebastien Roy 	} else {
5292b24ab6bSSebastien Roy 		retvalp->lr_err = dlmgmt_getattr_common(&linkp->ll_head,
5302b24ab6bSSebastien Roy 		    getattr->ld_attr, retvalp);
531d62bc4baSyz 	}
532d62bc4baSyz 	dlmgmt_table_unlock();
533d62bc4baSyz }
534d62bc4baSyz 
53532715170SCathy Zhou /* ARGSUSED */
536d62bc4baSyz static void
dlmgmt_createid(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)53732715170SCathy Zhou dlmgmt_createid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
53832715170SCathy Zhou     ucred_t *cred)
539d62bc4baSyz {
540024b0a25Sseb 	dlmgmt_door_createid_t	*createid = argp;
541024b0a25Sseb 	dlmgmt_createid_retval_t *retvalp = retp;
542024b0a25Sseb 	dlmgmt_link_t		*linkp;
543024b0a25Sseb 	datalink_id_t		linkid = DATALINK_INVALID_LINKID;
544024b0a25Sseb 	char			link[MAXLINKNAMELEN];
545024b0a25Sseb 	int			err;
546d62bc4baSyz 
547d62bc4baSyz 	/*
548d62bc4baSyz 	 * Hold the writer lock to update the dlconf table.
549d62bc4baSyz 	 */
550d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
551d62bc4baSyz 
5522b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(createid->ld_class, cred)) != 0)
5532b24ab6bSSebastien Roy 		goto done;
5542b24ab6bSSebastien Roy 
555d62bc4baSyz 	if (createid->ld_prefix) {
556d62bc4baSyz 		err = dlmgmt_generate_name(createid->ld_link, link,
5572b24ab6bSSebastien Roy 		    MAXLINKNAMELEN, zoneid);
558d62bc4baSyz 		if (err != 0)
559d62bc4baSyz 			goto done;
560d62bc4baSyz 
561d62bc4baSyz 		err = dlmgmt_create_common(link, createid->ld_class,
5622b24ab6bSSebastien Roy 		    createid->ld_media, zoneid, createid->ld_flags, &linkp);
563d62bc4baSyz 	} else {
564d62bc4baSyz 		err = dlmgmt_create_common(createid->ld_link,
5652b24ab6bSSebastien Roy 		    createid->ld_class, createid->ld_media, zoneid,
5662b24ab6bSSebastien Roy 		    createid->ld_flags, &linkp);
567d62bc4baSyz 	}
568d62bc4baSyz 
569d62bc4baSyz 	if (err == 0) {
570d62bc4baSyz 		/*
571d62bc4baSyz 		 * Keep the active mapping.
572d62bc4baSyz 		 */
573d62bc4baSyz 		linkid = linkp->ll_linkid;
5742b24ab6bSSebastien Roy 		if (createid->ld_flags & DLMGMT_ACTIVE) {
5752b24ab6bSSebastien Roy 			(void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
5762b24ab6bSSebastien Roy 			    DLMGMT_ACTIVE);
5772b24ab6bSSebastien Roy 		}
578d62bc4baSyz 	}
579d62bc4baSyz 
580d62bc4baSyz done:
581d62bc4baSyz 	dlmgmt_table_unlock();
582d62bc4baSyz 	retvalp->lr_linkid = linkid;
583d62bc4baSyz 	retvalp->lr_err = err;
584d62bc4baSyz }
585d62bc4baSyz 
58632715170SCathy Zhou /* ARGSUSED */
587d62bc4baSyz static void
dlmgmt_destroyid(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)58832715170SCathy Zhou dlmgmt_destroyid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
58932715170SCathy Zhou     ucred_t *cred)
590d62bc4baSyz {
591024b0a25Sseb 	dlmgmt_door_destroyid_t	*destroyid = argp;
592024b0a25Sseb 	dlmgmt_destroyid_retval_t *retvalp = retp;
593024b0a25Sseb 	datalink_id_t		linkid = destroyid->ld_linkid;
594024b0a25Sseb 	uint32_t		flags = destroyid->ld_flags;
595024b0a25Sseb 	dlmgmt_link_t		*linkp = NULL;
596024b0a25Sseb 	int			err = 0;
597d62bc4baSyz 
598d62bc4baSyz 	/*
599d62bc4baSyz 	 * Hold the writer lock to update the link table.
600d62bc4baSyz 	 */
601d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
6022b24ab6bSSebastien Roy 	if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
603d62bc4baSyz 		err = ENOENT;
604d62bc4baSyz 		goto done;
605d62bc4baSyz 	}
606d62bc4baSyz 
6072b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
608d62bc4baSyz 		goto done;
609d62bc4baSyz 
610d62bc4baSyz 	/*
611d62bc4baSyz 	 * Delete the active mapping.
612d62bc4baSyz 	 */
613d62bc4baSyz 	if (flags & DLMGMT_ACTIVE)
6142b24ab6bSSebastien Roy 		err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE);
6152b24ab6bSSebastien Roy 	if (err == 0)
6162b24ab6bSSebastien Roy 		err = dlmgmt_destroy_common(linkp, flags);
617d62bc4baSyz done:
618d62bc4baSyz 	dlmgmt_table_unlock();
619d62bc4baSyz 	retvalp->lr_err = err;
620d62bc4baSyz }
621d62bc4baSyz 
622d62bc4baSyz /*
623d62bc4baSyz  * Remap a linkid to a given link name, i.e., rename an existing link1
624d62bc4baSyz  * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to
625d62bc4baSyz  * the given link name.
626d62bc4baSyz  */
62732715170SCathy Zhou /* ARGSUSED */
628d62bc4baSyz static void
dlmgmt_remapid(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)62932715170SCathy Zhou dlmgmt_remapid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
63032715170SCathy Zhou     ucred_t *cred)
631d62bc4baSyz {
632024b0a25Sseb 	dlmgmt_door_remapid_t	*remapid = argp;
633024b0a25Sseb 	dlmgmt_remapid_retval_t	*retvalp = retp;
6342b24ab6bSSebastien Roy 	dlmgmt_link_t		*linkp;
6352b24ab6bSSebastien Roy 	char			oldname[MAXLINKNAMELEN];
6362b24ab6bSSebastien Roy 	boolean_t		renamed = B_FALSE;
637024b0a25Sseb 	int			err = 0;
638d62bc4baSyz 
639d62bc4baSyz 	if (!dladm_valid_linkname(remapid->ld_link)) {
640d62bc4baSyz 		retvalp->lr_err = EINVAL;
641d62bc4baSyz 		return;
642d62bc4baSyz 	}
643d62bc4baSyz 
644d62bc4baSyz 	/*
645d62bc4baSyz 	 * Hold the writer lock to update the link table.
646d62bc4baSyz 	 */
647d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
6482b24ab6bSSebastien Roy 	if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) {
649d62bc4baSyz 		err = ENOENT;
650d62bc4baSyz 		goto done;
651d62bc4baSyz 	}
652d62bc4baSyz 
6532b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
6542b24ab6bSSebastien Roy 		goto done;
6552b24ab6bSSebastien Roy 
6562b24ab6bSSebastien Roy 	if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
657d62bc4baSyz 		err = EEXIST;
658d62bc4baSyz 		goto done;
659d62bc4baSyz 	}
660d62bc4baSyz 
6612b24ab6bSSebastien Roy 	(void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN);
6622b24ab6bSSebastien Roy 	avl_remove(&dlmgmt_name_avl, linkp);
6632b24ab6bSSebastien Roy 	(void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN);
6642b24ab6bSSebastien Roy 	avl_add(&dlmgmt_name_avl, linkp);
6652b24ab6bSSebastien Roy 	renamed = B_TRUE;
666d62bc4baSyz 
6672b24ab6bSSebastien Roy 	if (linkp->ll_flags & DLMGMT_ACTIVE) {
6682b24ab6bSSebastien Roy 		err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE);
6692b24ab6bSSebastien Roy 		if (err != 0)
6702b24ab6bSSebastien Roy 			goto done;
6712b24ab6bSSebastien Roy 	}
6722b24ab6bSSebastien Roy 	if (linkp->ll_flags & DLMGMT_PERSIST) {
6732b24ab6bSSebastien Roy 		err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST);
6742b24ab6bSSebastien Roy 		if (err != 0) {
6752b24ab6bSSebastien Roy 			if (linkp->ll_flags & DLMGMT_ACTIVE) {
6762b24ab6bSSebastien Roy 				(void) dlmgmt_write_db_entry(remapid->ld_link,
6772b24ab6bSSebastien Roy 				    linkp, DLMGMT_ACTIVE);
6782b24ab6bSSebastien Roy 			}
6792b24ab6bSSebastien Roy 			goto done;
6802b24ab6bSSebastien Roy 		}
6812b24ab6bSSebastien Roy 	}
6822b24ab6bSSebastien Roy 
6832b24ab6bSSebastien Roy 	dlmgmt_advance(linkp);
6842b24ab6bSSebastien Roy 	linkp->ll_gen++;
685d62bc4baSyz done:
6862b24ab6bSSebastien Roy 	if (err != 0 && renamed) {
6872b24ab6bSSebastien Roy 		avl_remove(&dlmgmt_name_avl, linkp);
6882b24ab6bSSebastien Roy 		(void) strlcpy(linkp->ll_link, oldname, MAXLINKNAMELEN);
6892b24ab6bSSebastien Roy 		avl_add(&dlmgmt_name_avl, linkp);
6902b24ab6bSSebastien Roy 	}
691d62bc4baSyz 	dlmgmt_table_unlock();
692d62bc4baSyz 	retvalp->lr_err = err;
693d62bc4baSyz }
694d62bc4baSyz 
69532715170SCathy Zhou /* ARGSUSED */
696d62bc4baSyz static void
dlmgmt_upid(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)69732715170SCathy Zhou dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
69832715170SCathy Zhou     ucred_t *cred)
699d62bc4baSyz {
700024b0a25Sseb 	dlmgmt_door_upid_t	*upid = argp;
701024b0a25Sseb 	dlmgmt_upid_retval_t	*retvalp = retp;
702024b0a25Sseb 	dlmgmt_link_t		*linkp;
703024b0a25Sseb 	int			err = 0;
704d62bc4baSyz 
705d62bc4baSyz 	/*
706d62bc4baSyz 	 * Hold the writer lock to update the link table.
707d62bc4baSyz 	 */
708d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
7092b24ab6bSSebastien Roy 	if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) {
710d62bc4baSyz 		err = ENOENT;
711d62bc4baSyz 		goto done;
712d62bc4baSyz 	}
713d62bc4baSyz 
7142b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
7152b24ab6bSSebastien Roy 		goto done;
7162b24ab6bSSebastien Roy 
717d62bc4baSyz 	if (linkp->ll_flags & DLMGMT_ACTIVE) {
718d62bc4baSyz 		err = EINVAL;
719d62bc4baSyz 		goto done;
720d62bc4baSyz 	}
721d62bc4baSyz 
7222b24ab6bSSebastien Roy 	if ((err = link_activate(linkp)) == 0) {
7232b24ab6bSSebastien Roy 		(void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
7242b24ab6bSSebastien Roy 		    DLMGMT_ACTIVE);
7252b24ab6bSSebastien Roy 	}
726d62bc4baSyz done:
727d62bc4baSyz 	dlmgmt_table_unlock();
728d62bc4baSyz 	retvalp->lr_err = err;
729d62bc4baSyz }
730d62bc4baSyz 
73132715170SCathy Zhou /* ARGSUSED */
732d62bc4baSyz static void
dlmgmt_createconf(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)73332715170SCathy Zhou dlmgmt_createconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
73432715170SCathy Zhou     ucred_t *cred)
735d62bc4baSyz {
736024b0a25Sseb 	dlmgmt_door_createconf_t *createconf = argp;
737024b0a25Sseb 	dlmgmt_createconf_retval_t *retvalp = retp;
7382b24ab6bSSebastien Roy 	dlmgmt_dlconf_t		*dlconfp;
739024b0a25Sseb 	int			err;
740d62bc4baSyz 
741d62bc4baSyz 	/*
742d62bc4baSyz 	 * Hold the writer lock to update the dlconf table.
743d62bc4baSyz 	 */
744d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_TRUE);
745d62bc4baSyz 
7462b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(createconf->ld_class, cred)) != 0)
747d62bc4baSyz 		goto done;
748d62bc4baSyz 
7492b24ab6bSSebastien Roy 	err = dlconf_create(createconf->ld_link, createconf->ld_linkid,
7502b24ab6bSSebastien Roy 	    createconf->ld_class, createconf->ld_media, zoneid, &dlconfp);
7512b24ab6bSSebastien Roy 	if (err == 0) {
7522b24ab6bSSebastien Roy 		avl_add(&dlmgmt_dlconf_avl, dlconfp);
7532b24ab6bSSebastien Roy 		dlmgmt_advance_dlconfid(dlconfp);
75432715170SCathy Zhou 		retvalp->lr_confid = dlconfp->ld_id;
7552b24ab6bSSebastien Roy 	}
756d62bc4baSyz done:
757d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
758d62bc4baSyz 	retvalp->lr_err = err;
759d62bc4baSyz }
760d62bc4baSyz 
76132715170SCathy Zhou /* ARGSUSED */
762d62bc4baSyz static void
dlmgmt_setattr(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)76332715170SCathy Zhou dlmgmt_setattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
76432715170SCathy Zhou     ucred_t *cred)
765d62bc4baSyz {
766024b0a25Sseb 	dlmgmt_door_setattr_t	*setattr = argp;
767024b0a25Sseb 	dlmgmt_setattr_retval_t	*retvalp = retp;
768024b0a25Sseb 	dlmgmt_dlconf_t		dlconf, *dlconfp;
769024b0a25Sseb 	int			err = 0;
770d62bc4baSyz 
771d62bc4baSyz 	/*
772d62bc4baSyz 	 * Hold the writer lock to update the dlconf table.
773d62bc4baSyz 	 */
774d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_TRUE);
775d62bc4baSyz 
77632715170SCathy Zhou 	dlconf.ld_id = setattr->ld_confid;
777d62bc4baSyz 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
7782b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
779d62bc4baSyz 		err = ENOENT;
780d62bc4baSyz 		goto done;
781d62bc4baSyz 	}
782d62bc4baSyz 
7832b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
7842b24ab6bSSebastien Roy 		goto done;
7852b24ab6bSSebastien Roy 
786d62bc4baSyz 	err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr,
787d62bc4baSyz 	    &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type);
788d62bc4baSyz 
789d62bc4baSyz done:
790d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
791d62bc4baSyz 	retvalp->lr_err = err;
792d62bc4baSyz }
793d62bc4baSyz 
79432715170SCathy Zhou /* ARGSUSED */
795d62bc4baSyz static void
dlmgmt_unsetconfattr(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)79632715170SCathy Zhou dlmgmt_unsetconfattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
79732715170SCathy Zhou     ucred_t *cred)
798d62bc4baSyz {
799024b0a25Sseb 	dlmgmt_door_unsetattr_t	*unsetattr = argp;
800024b0a25Sseb 	dlmgmt_unsetattr_retval_t *retvalp = retp;
801024b0a25Sseb 	dlmgmt_dlconf_t		dlconf, *dlconfp;
802024b0a25Sseb 	int			err = 0;
803d62bc4baSyz 
804d62bc4baSyz 	/*
805d62bc4baSyz 	 * Hold the writer lock to update the dlconf table.
806d62bc4baSyz 	 */
807d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_TRUE);
808d62bc4baSyz 
80932715170SCathy Zhou 	dlconf.ld_id = unsetattr->ld_confid;
810d62bc4baSyz 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
8112b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
812d62bc4baSyz 		err = ENOENT;
813d62bc4baSyz 		goto done;
814d62bc4baSyz 	}
815d62bc4baSyz 
8162b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
8172b24ab6bSSebastien Roy 		goto done;
8182b24ab6bSSebastien Roy 
8192b24ab6bSSebastien Roy 	linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr);
820d62bc4baSyz 
821d62bc4baSyz done:
822d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
823d62bc4baSyz 	retvalp->lr_err = err;
824d62bc4baSyz }
825d62bc4baSyz 
826d62bc4baSyz /*
82732715170SCathy Zhou  * Note that dlmgmt_openconf() returns a conf ID of a conf AVL tree entry,
828d62bc4baSyz  * which is managed by dlmgmtd.  The ID is used to find the conf entry when
829d62bc4baSyz  * dlmgmt_write_conf() is called.  The conf entry contains an ld_gen value
830d62bc4baSyz  * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time
83132715170SCathy Zhou  * of dlmgmt_openconf(), and ll_gen changes every time the dlmgmt_link_t
832d62bc4baSyz  * changes its attributes.  Therefore, dlmgmt_write_conf() can compare ld_gen
833d62bc4baSyz  * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if
83432715170SCathy Zhou  * anything has changed between the dlmgmt_openconf() and dlmgmt_writeconf()
835d62bc4baSyz  * calls.  If so, EAGAIN is returned.  This mechanism can ensures atomicity
836d62bc4baSyz  * across the pair of dladm_read_conf() and dladm_write_conf() calls.
837d62bc4baSyz  */
83832715170SCathy Zhou /* ARGSUSED */
839d62bc4baSyz static void
dlmgmt_writeconf(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)84032715170SCathy Zhou dlmgmt_writeconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
84132715170SCathy Zhou     ucred_t *cred)
842d62bc4baSyz {
843024b0a25Sseb 	dlmgmt_door_writeconf_t	*writeconf = argp;
844024b0a25Sseb 	dlmgmt_writeconf_retval_t *retvalp = retp;
845d62bc4baSyz 	dlmgmt_dlconf_t		dlconf, *dlconfp;
846d62bc4baSyz 	dlmgmt_link_t		*linkp;
847d62bc4baSyz 	dlmgmt_linkattr_t	*attrp, *next;
848d62bc4baSyz 	int			err = 0;
849d62bc4baSyz 
850d62bc4baSyz 	/*
85132715170SCathy Zhou 	 * Hold the lock to access the dlconf table.
852d62bc4baSyz 	 */
853d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_TRUE);
854d62bc4baSyz 
85532715170SCathy Zhou 	dlconf.ld_id = writeconf->ld_confid;
856d62bc4baSyz 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
8572b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
858d62bc4baSyz 		err = ENOENT;
859d62bc4baSyz 		goto done;
860d62bc4baSyz 	}
861d62bc4baSyz 
8622b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
8632b24ab6bSSebastien Roy 		goto done;
8642b24ab6bSSebastien Roy 
865d62bc4baSyz 	/*
866d62bc4baSyz 	 * Hold the writer lock to update the link table.
867d62bc4baSyz 	 */
868d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
8692b24ab6bSSebastien Roy 	linkp = link_by_id(dlconfp->ld_linkid, zoneid);
870d62bc4baSyz 	if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) ||
871d62bc4baSyz 	    (linkp->ll_media != dlconfp->ld_media) ||
872d62bc4baSyz 	    (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) {
873d62bc4baSyz 		/*
874d62bc4baSyz 		 * The link does not exist.
875d62bc4baSyz 		 */
876d62bc4baSyz 		dlmgmt_table_unlock();
877d62bc4baSyz 		err = ENOENT;
878d62bc4baSyz 		goto done;
879d62bc4baSyz 	}
880d62bc4baSyz 
881d62bc4baSyz 	if (linkp->ll_gen != dlconfp->ld_gen) {
882d62bc4baSyz 		/*
883d62bc4baSyz 		 * Something has changed the link configuration; try again.
884d62bc4baSyz 		 */
885d62bc4baSyz 		dlmgmt_table_unlock();
886d62bc4baSyz 		err = EAGAIN;
887d62bc4baSyz 		goto done;
888d62bc4baSyz 	}
889d62bc4baSyz 
890d62bc4baSyz 	/*
891d62bc4baSyz 	 * Delete the old attribute list.
892d62bc4baSyz 	 */
893d62bc4baSyz 	for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
894d62bc4baSyz 		next = attrp->lp_next;
895d62bc4baSyz 		free(attrp->lp_val);
896d62bc4baSyz 		free(attrp);
897d62bc4baSyz 	}
898d62bc4baSyz 	linkp->ll_head = NULL;
899d62bc4baSyz 
900d62bc4baSyz 	/*
901d62bc4baSyz 	 * Set the new attribute.
902d62bc4baSyz 	 */
903d62bc4baSyz 	for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) {
904d62bc4baSyz 		if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name,
905d62bc4baSyz 		    attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) {
906d62bc4baSyz 			dlmgmt_table_unlock();
907d62bc4baSyz 			goto done;
908d62bc4baSyz 		}
909d62bc4baSyz 	}
910d62bc4baSyz 
911d62bc4baSyz 	linkp->ll_gen++;
9122b24ab6bSSebastien Roy 	err = dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_PERSIST);
913d62bc4baSyz 	dlmgmt_table_unlock();
914d62bc4baSyz done:
915d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
916d62bc4baSyz 	retvalp->lr_err = err;
917d62bc4baSyz }
918d62bc4baSyz 
91932715170SCathy Zhou /* ARGSUSED */
920d62bc4baSyz static void
dlmgmt_removeconf(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)92132715170SCathy Zhou dlmgmt_removeconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
92232715170SCathy Zhou     ucred_t *cred)
923d62bc4baSyz {
924*85dff7a0SAndy Fiddaman 	dlmgmt_door_removeconf_t	*removeconf = argp;
925024b0a25Sseb 	dlmgmt_removeconf_retval_t	*retvalp = retp;
9262b24ab6bSSebastien Roy 	dlmgmt_link_t			*linkp;
927024b0a25Sseb 	int				err;
928d62bc4baSyz 
929d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
9302b24ab6bSSebastien Roy 	if ((linkp = link_by_id(removeconf->ld_linkid, zoneid)) == NULL) {
9312b24ab6bSSebastien Roy 		err = ENOENT;
9322b24ab6bSSebastien Roy 		goto done;
9332b24ab6bSSebastien Roy 	}
9342b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID && linkp->ll_onloan) {
9352b24ab6bSSebastien Roy 		/*
9362b24ab6bSSebastien Roy 		 * A non-global zone cannot remove the persistent
9372b24ab6bSSebastien Roy 		 * configuration of a link that is on loan from the global
9382b24ab6bSSebastien Roy 		 * zone.
9392b24ab6bSSebastien Roy 		 */
9402b24ab6bSSebastien Roy 		err = EACCES;
9412b24ab6bSSebastien Roy 		goto done;
9422b24ab6bSSebastien Roy 	}
9432b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
9442b24ab6bSSebastien Roy 		goto done;
9452b24ab6bSSebastien Roy 
9462b24ab6bSSebastien Roy 	err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST);
9472b24ab6bSSebastien Roy done:
948d62bc4baSyz 	dlmgmt_table_unlock();
949d62bc4baSyz 	retvalp->lr_err = err;
950d62bc4baSyz }
951d62bc4baSyz 
95232715170SCathy Zhou /* ARGSUSED */
953d62bc4baSyz static void
dlmgmt_destroyconf(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)95432715170SCathy Zhou dlmgmt_destroyconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
95532715170SCathy Zhou     ucred_t *cred)
956d62bc4baSyz {
957024b0a25Sseb 	dlmgmt_door_destroyconf_t	*destroyconf = argp;
958024b0a25Sseb 	dlmgmt_destroyconf_retval_t	*retvalp = retp;
959024b0a25Sseb 	dlmgmt_dlconf_t			dlconf, *dlconfp;
960024b0a25Sseb 	int				err = 0;
961d62bc4baSyz 
962d62bc4baSyz 	/*
963d62bc4baSyz 	 * Hold the writer lock to update the dlconf table.
964d62bc4baSyz 	 */
965d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_TRUE);
966d62bc4baSyz 
96732715170SCathy Zhou 	dlconf.ld_id = destroyconf->ld_confid;
968d62bc4baSyz 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
9692b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
970d62bc4baSyz 		err = ENOENT;
971d62bc4baSyz 		goto done;
972d62bc4baSyz 	}
973d62bc4baSyz 
9742b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
9752b24ab6bSSebastien Roy 		goto done;
9762b24ab6bSSebastien Roy 
977d62bc4baSyz 	avl_remove(&dlmgmt_dlconf_avl, dlconfp);
978d62bc4baSyz 	dlconf_destroy(dlconfp);
979d62bc4baSyz 
980d62bc4baSyz done:
981d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
982d62bc4baSyz 	retvalp->lr_err = err;
983d62bc4baSyz }
984d62bc4baSyz 
985d62bc4baSyz /*
98632715170SCathy Zhou  * dlmgmt_openconf() returns a handle of the current configuration, which
98732715170SCathy Zhou  * is then used to update the configuration by dlmgmt_writeconf(). Therefore,
98832715170SCathy Zhou  * it requires privileges.
98932715170SCathy Zhou  *
99032715170SCathy Zhou  * Further, please see the comments above dladm_write_conf() to see how
99132715170SCathy Zhou  * ld_gen is used to ensure atomicity across the {dlmgmt_openconf(),
99232715170SCathy Zhou  * dlmgmt_writeconf()} pair.
993d62bc4baSyz  */
9942b24ab6bSSebastien Roy /* ARGSUSED */
995d62bc4baSyz static void
dlmgmt_openconf(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)99632715170SCathy Zhou dlmgmt_openconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
99732715170SCathy Zhou     ucred_t *cred)
998d62bc4baSyz {
99932715170SCathy Zhou 	dlmgmt_door_openconf_t	*openconf = argp;
100032715170SCathy Zhou 	dlmgmt_openconf_retval_t *retvalp = retp;
1001*85dff7a0SAndy Fiddaman 	dlmgmt_link_t		*linkp;
100232715170SCathy Zhou 	datalink_id_t		linkid = openconf->ld_linkid;
10032b24ab6bSSebastien Roy 	dlmgmt_dlconf_t		*dlconfp;
1004d62bc4baSyz 	dlmgmt_linkattr_t	*attrp;
1005d62bc4baSyz 	int			err = 0;
1006d62bc4baSyz 
1007d62bc4baSyz 	/*
1008d62bc4baSyz 	 * Hold the writer lock to update the dlconf table.
1009d62bc4baSyz 	 */
1010d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_TRUE);
1011d62bc4baSyz 
1012d62bc4baSyz 	/*
1013d62bc4baSyz 	 * Hold the reader lock to access the link
1014d62bc4baSyz 	 */
1015d62bc4baSyz 	dlmgmt_table_lock(B_FALSE);
10162b24ab6bSSebastien Roy 	linkp = link_by_id(linkid, zoneid);
1017d62bc4baSyz 	if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) {
10182b24ab6bSSebastien Roy 		/* The persistent link configuration does not exist. */
10192b24ab6bSSebastien Roy 		err = ENOENT;
10202b24ab6bSSebastien Roy 		goto done;
10212b24ab6bSSebastien Roy 	}
10222b24ab6bSSebastien Roy 	if (linkp->ll_onloan && zoneid != GLOBAL_ZONEID) {
1023d62bc4baSyz 		/*
10242b24ab6bSSebastien Roy 		 * The caller is in a non-global zone and the persistent
10252b24ab6bSSebastien Roy 		 * configuration belongs to the global zone.
1026d62bc4baSyz 		 */
10272b24ab6bSSebastien Roy 		err = EACCES;
1028d62bc4baSyz 		goto done;
1029d62bc4baSyz 	}
1030d62bc4baSyz 
103132715170SCathy Zhou 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
103232715170SCathy Zhou 		goto done;
103332715170SCathy Zhou 
1034d62bc4baSyz 	if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid,
10352b24ab6bSSebastien Roy 	    linkp->ll_class, linkp->ll_media, zoneid, &dlconfp)) != 0)
1036d62bc4baSyz 		goto done;
1037d62bc4baSyz 
1038d62bc4baSyz 	for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) {
1039d62bc4baSyz 		if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name,
1040d62bc4baSyz 		    attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) {
1041d62bc4baSyz 			dlconf_destroy(dlconfp);
1042d62bc4baSyz 			goto done;
1043d62bc4baSyz 		}
1044d62bc4baSyz 	}
1045d62bc4baSyz 	dlconfp->ld_gen = linkp->ll_gen;
10462b24ab6bSSebastien Roy 	avl_add(&dlmgmt_dlconf_avl, dlconfp);
1047d62bc4baSyz 	dlmgmt_advance_dlconfid(dlconfp);
1048d62bc4baSyz 
104932715170SCathy Zhou 	retvalp->lr_confid = dlconfp->ld_id;
1050d62bc4baSyz done:
1051d62bc4baSyz 	dlmgmt_table_unlock();
1052d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
1053d62bc4baSyz 	retvalp->lr_err = err;
1054d62bc4baSyz }
1055d62bc4baSyz 
1056d62bc4baSyz /*
105732715170SCathy Zhou  * dlmgmt_getconfsnapshot() returns a read-only snapshot of all the
105832715170SCathy Zhou  * configuration, and requires no privileges.
105932715170SCathy Zhou  *
106032715170SCathy Zhou  * If the given size cannot hold all the configuration, set the size
106132715170SCathy Zhou  * that is needed, and return ENOSPC.
1062d62bc4baSyz  */
10632b24ab6bSSebastien Roy /* ARGSUSED */
1064024b0a25Sseb static void
dlmgmt_getconfsnapshot(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)106532715170SCathy Zhou dlmgmt_getconfsnapshot(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
106632715170SCathy Zhou     ucred_t *cred)
106732715170SCathy Zhou {
106832715170SCathy Zhou 	dlmgmt_door_getconfsnapshot_t	*snapshot = argp;
106932715170SCathy Zhou 	dlmgmt_getconfsnapshot_retval_t	*retvalp = retp;
1070*85dff7a0SAndy Fiddaman 	dlmgmt_link_t			*linkp;
107132715170SCathy Zhou 	datalink_id_t			linkid = snapshot->ld_linkid;
107232715170SCathy Zhou 	dlmgmt_linkattr_t		*attrp;
107332715170SCathy Zhou 	char				*buf;
107432715170SCathy Zhou 	size_t				nvlsz;
107532715170SCathy Zhou 	nvlist_t			*nvl = NULL;
107632715170SCathy Zhou 	int				err = 0;
107732715170SCathy Zhou 
107832715170SCathy Zhou 	assert(*sz >= sizeof (dlmgmt_getconfsnapshot_retval_t));
107932715170SCathy Zhou 
108032715170SCathy Zhou 	/*
108132715170SCathy Zhou 	 * Hold the reader lock to access the link
108232715170SCathy Zhou 	 */
108332715170SCathy Zhou 	dlmgmt_table_lock(B_FALSE);
108432715170SCathy Zhou 	linkp = link_by_id(linkid, zoneid);
108532715170SCathy Zhou 	if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) {
108632715170SCathy Zhou 		/* The persistent link configuration does not exist. */
108732715170SCathy Zhou 		err = ENOENT;
108832715170SCathy Zhou 		goto done;
108932715170SCathy Zhou 	}
109032715170SCathy Zhou 	if (linkp->ll_onloan && zoneid != GLOBAL_ZONEID) {
109132715170SCathy Zhou 		/*
109232715170SCathy Zhou 		 * The caller is in a non-global zone and the persistent
109332715170SCathy Zhou 		 * configuration belongs to the global zone.
109432715170SCathy Zhou 		 */
109532715170SCathy Zhou 		err = EACCES;
109632715170SCathy Zhou 		goto done;
109732715170SCathy Zhou 	}
109832715170SCathy Zhou 
109932715170SCathy Zhou 	err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0);
110032715170SCathy Zhou 	if (err != 0)
110132715170SCathy Zhou 		goto done;
110232715170SCathy Zhou 
110332715170SCathy Zhou 	for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) {
110432715170SCathy Zhou 		if ((err = nvlist_add_byte_array(nvl, attrp->lp_name,
110532715170SCathy Zhou 		    attrp->lp_val, attrp->lp_sz)) != 0) {
110632715170SCathy Zhou 			goto done;
110732715170SCathy Zhou 		}
110832715170SCathy Zhou 	}
110932715170SCathy Zhou 
111032715170SCathy Zhou 	if ((err = nvlist_size(nvl, &nvlsz, NV_ENCODE_NATIVE)) != 0)
111132715170SCathy Zhou 		goto done;
111232715170SCathy Zhou 
111332715170SCathy Zhou 	if (nvlsz + sizeof (dlmgmt_getconfsnapshot_retval_t) > *sz) {
111432715170SCathy Zhou 		*sz = nvlsz + sizeof (dlmgmt_getconfsnapshot_retval_t);
111532715170SCathy Zhou 		err = ENOSPC;
111632715170SCathy Zhou 		goto done;
111732715170SCathy Zhou 	}
111832715170SCathy Zhou 
111932715170SCathy Zhou 	/*
112032715170SCathy Zhou 	 * pack the the nvlist into the return value.
112132715170SCathy Zhou 	 */
112232715170SCathy Zhou 	*sz = nvlsz + sizeof (dlmgmt_getconfsnapshot_retval_t);
112332715170SCathy Zhou 	retvalp->lr_nvlsz = nvlsz;
112432715170SCathy Zhou 	buf = (char *)retvalp + sizeof (dlmgmt_getconfsnapshot_retval_t);
112532715170SCathy Zhou 	err = nvlist_pack(nvl, &buf, &nvlsz, NV_ENCODE_NATIVE, 0);
112632715170SCathy Zhou 
112732715170SCathy Zhou done:
112832715170SCathy Zhou 	dlmgmt_table_unlock();
112932715170SCathy Zhou 	nvlist_free(nvl);
113032715170SCathy Zhou 	retvalp->lr_err = err;
113132715170SCathy Zhou }
113232715170SCathy Zhou 
113332715170SCathy Zhou /* ARGSUSED */
113432715170SCathy Zhou static void
dlmgmt_getattr(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)113532715170SCathy Zhou dlmgmt_getattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
113632715170SCathy Zhou     ucred_t *cred)
1137d62bc4baSyz {
1138024b0a25Sseb 	dlmgmt_door_getattr_t	*getattr = argp;
1139024b0a25Sseb 	dlmgmt_getattr_retval_t	*retvalp = retp;
1140d62bc4baSyz 	dlmgmt_dlconf_t		dlconf, *dlconfp;
114132715170SCathy Zhou 	int			err;
1142d62bc4baSyz 
1143d62bc4baSyz 	/*
1144024b0a25Sseb 	 * Hold the read lock to access the dlconf table.
1145d62bc4baSyz 	 */
1146d62bc4baSyz 	dlmgmt_dlconf_table_lock(B_FALSE);
1147d62bc4baSyz 
114832715170SCathy Zhou 	dlconf.ld_id = getattr->ld_confid;
11492b24ab6bSSebastien Roy 	if ((dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL)) == NULL ||
11502b24ab6bSSebastien Roy 	    zoneid != dlconfp->ld_zoneid) {
1151024b0a25Sseb 		retvalp->lr_err = ENOENT;
11522b24ab6bSSebastien Roy 	} else {
115332715170SCathy Zhou 		if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) {
115432715170SCathy Zhou 			retvalp->lr_err = err;
115532715170SCathy Zhou 		} else {
115632715170SCathy Zhou 			retvalp->lr_err = dlmgmt_getattr_common(
115732715170SCathy Zhou 			    &dlconfp->ld_head, getattr->ld_attr, retvalp);
115832715170SCathy Zhou 		}
1159d62bc4baSyz 	}
1160d62bc4baSyz 
1161d62bc4baSyz 	dlmgmt_dlconf_table_unlock();
1162d62bc4baSyz }
1163d62bc4baSyz 
116432715170SCathy Zhou /* ARGSUSED */
116530890389Sartem static void
dlmgmt_upcall_linkprop_init(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)116632715170SCathy Zhou dlmgmt_upcall_linkprop_init(void *argp, void *retp, size_t *sz,
116732715170SCathy Zhou     zoneid_t zoneid, ucred_t *cred)
116830890389Sartem {
116930890389Sartem 	dlmgmt_door_linkprop_init_t	*lip = argp;
117030890389Sartem 	dlmgmt_linkprop_init_retval_t	*retvalp = retp;
11712b24ab6bSSebastien Roy 	dlmgmt_link_t			*linkp;
11722b24ab6bSSebastien Roy 	int				err;
117330890389Sartem 
117430890389Sartem 	dlmgmt_table_lock(B_FALSE);
11752b24ab6bSSebastien Roy 	if ((linkp = link_by_id(lip->ld_linkid, zoneid)) == NULL)
11762b24ab6bSSebastien Roy 		err = ENOENT;
117730890389Sartem 	else
11782b24ab6bSSebastien Roy 		err = dlmgmt_checkprivs(linkp->ll_class, cred);
117930890389Sartem 	dlmgmt_table_unlock();
118030890389Sartem 
11818d4cf8d8S 	if (err == 0) {
11828d4cf8d8S 		dladm_status_t	s;
11838d4cf8d8S 		char		buf[DLADM_STRSIZE];
11848d4cf8d8S 
11858d4cf8d8S 		s = dladm_init_linkprop(dld_handle, lip->ld_linkid, B_TRUE);
11868d4cf8d8S 		if (s != DLADM_STATUS_OK) {
11878d4cf8d8S 			dlmgmt_log(LOG_WARNING,
11888d4cf8d8S 			    "linkprop initialization failed on link %d: %s",
11898d4cf8d8S 			    lip->ld_linkid, dladm_status2str(s, buf));
11908d4cf8d8S 			err = EINVAL;
11918d4cf8d8S 		}
11928d4cf8d8S 	}
11932b24ab6bSSebastien Roy 	retvalp->lr_err = err;
119430890389Sartem }
119530890389Sartem 
11962b24ab6bSSebastien Roy /* ARGSUSED */
119762ee1d25SArtem Kachitchkine static void
dlmgmt_setzoneid(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)119832715170SCathy Zhou dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
119932715170SCathy Zhou     ucred_t *cred)
12002b24ab6bSSebastien Roy {
12012b24ab6bSSebastien Roy 	dlmgmt_door_setzoneid_t	*setzoneid = argp;
12022b24ab6bSSebastien Roy 	dlmgmt_setzoneid_retval_t *retvalp = retp;
12032b24ab6bSSebastien Roy 	dlmgmt_link_t		*linkp;
12042b24ab6bSSebastien Roy 	datalink_id_t		linkid = setzoneid->ld_linkid;
12052b24ab6bSSebastien Roy 	zoneid_t		oldzoneid, newzoneid;
12062b24ab6bSSebastien Roy 	int			err = 0;
12072b24ab6bSSebastien Roy 
12082b24ab6bSSebastien Roy 	dlmgmt_table_lock(B_TRUE);
12092b24ab6bSSebastien Roy 
12102b24ab6bSSebastien Roy 	/* We currently only allow changing zoneid's from the global zone. */
12112b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID) {
12122b24ab6bSSebastien Roy 		err = EACCES;
12132b24ab6bSSebastien Roy 		goto done;
12142b24ab6bSSebastien Roy 	}
12152b24ab6bSSebastien Roy 
12162b24ab6bSSebastien Roy 	if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
12172b24ab6bSSebastien Roy 		err = ENOENT;
12182b24ab6bSSebastien Roy 		goto done;
12192b24ab6bSSebastien Roy 	}
12202b24ab6bSSebastien Roy 
12212b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
12222b24ab6bSSebastien Roy 		goto done;
12232b24ab6bSSebastien Roy 
12242b24ab6bSSebastien Roy 	/* We can only assign an active link to a zone. */
12252b24ab6bSSebastien Roy 	if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
12262b24ab6bSSebastien Roy 		err = EINVAL;
12272b24ab6bSSebastien Roy 		goto done;
12282b24ab6bSSebastien Roy 	}
12292b24ab6bSSebastien Roy 
12302b24ab6bSSebastien Roy 	oldzoneid = linkp->ll_zoneid;
12312b24ab6bSSebastien Roy 	newzoneid = setzoneid->ld_zoneid;
12322b24ab6bSSebastien Roy 
12332b24ab6bSSebastien Roy 	if (oldzoneid == newzoneid)
12342b24ab6bSSebastien Roy 		goto done;
12352b24ab6bSSebastien Roy 
12362b24ab6bSSebastien Roy 	/*
12372b24ab6bSSebastien Roy 	 * Before we remove the link from its current zone, make sure that
12382b24ab6bSSebastien Roy 	 * there isn't a link with the same name in the destination zone.
12392b24ab6bSSebastien Roy 	 */
12400617d817SDan McDonald 	if (zoneid != GLOBAL_ZONEID &&
12410617d817SDan McDonald 	    link_by_name(linkp->ll_link, newzoneid) != NULL) {
12422b24ab6bSSebastien Roy 		err = EEXIST;
12432b24ab6bSSebastien Roy 		goto done;
12442b24ab6bSSebastien Roy 	}
12452b24ab6bSSebastien Roy 
12462b24ab6bSSebastien Roy 	if (oldzoneid != GLOBAL_ZONEID) {
12472b24ab6bSSebastien Roy 		if (zone_remove_datalink(oldzoneid, linkid) != 0) {
12482b24ab6bSSebastien Roy 			err = errno;
12492b24ab6bSSebastien Roy 			dlmgmt_log(LOG_WARNING, "unable to remove link %d from "
12502b24ab6bSSebastien Roy 			    "zone %d: %s", linkid, oldzoneid, strerror(err));
12512b24ab6bSSebastien Roy 			goto done;
12522b24ab6bSSebastien Roy 		}
1253*85dff7a0SAndy Fiddaman 		if (linkp->ll_onloan) {
1254*85dff7a0SAndy Fiddaman 			avl_remove(&dlmgmt_loan_avl, linkp);
1255*85dff7a0SAndy Fiddaman 			linkp->ll_onloan = B_FALSE;
1256*85dff7a0SAndy Fiddaman 		}
12572b24ab6bSSebastien Roy 	}
12582b24ab6bSSebastien Roy 	if (newzoneid != GLOBAL_ZONEID) {
12592b24ab6bSSebastien Roy 		if (zone_add_datalink(newzoneid, linkid) != 0) {
12602b24ab6bSSebastien Roy 			err = errno;
12612b24ab6bSSebastien Roy 			dlmgmt_log(LOG_WARNING, "unable to add link %d to zone "
12622b24ab6bSSebastien Roy 			    "%d: %s", linkid, newzoneid, strerror(err));
12632b24ab6bSSebastien Roy 			(void) zone_add_datalink(oldzoneid, linkid);
12642b24ab6bSSebastien Roy 			goto done;
12652b24ab6bSSebastien Roy 		}
1266*85dff7a0SAndy Fiddaman 		if (!linkp->ll_transient) {
1267*85dff7a0SAndy Fiddaman 			avl_add(&dlmgmt_loan_avl, linkp);
1268*85dff7a0SAndy Fiddaman 			linkp->ll_onloan = B_TRUE;
1269*85dff7a0SAndy Fiddaman 		}
12702b24ab6bSSebastien Roy 	}
12712b24ab6bSSebastien Roy 
12722b24ab6bSSebastien Roy 	avl_remove(&dlmgmt_name_avl, linkp);
12732b24ab6bSSebastien Roy 	linkp->ll_zoneid = newzoneid;
12742b24ab6bSSebastien Roy 	avl_add(&dlmgmt_name_avl, linkp);
12752b24ab6bSSebastien Roy 
12762b24ab6bSSebastien Roy done:
12772b24ab6bSSebastien Roy 	dlmgmt_table_unlock();
12782b24ab6bSSebastien Roy 	retvalp->lr_err = err;
12792b24ab6bSSebastien Roy }
12802b24ab6bSSebastien Roy 
128132715170SCathy Zhou /* ARGSUSED */
12822b24ab6bSSebastien Roy static void
dlmgmt_zoneboot(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)128332715170SCathy Zhou dlmgmt_zoneboot(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
128432715170SCathy Zhou     ucred_t *cred)
12852b24ab6bSSebastien Roy {
12862b24ab6bSSebastien Roy 	int			err;
12872b24ab6bSSebastien Roy 	dlmgmt_door_zoneboot_t	*zoneboot = argp;
12882b24ab6bSSebastien Roy 	dlmgmt_zoneboot_retval_t *retvalp = retp;
12892b24ab6bSSebastien Roy 
12902b24ab6bSSebastien Roy 	dlmgmt_table_lock(B_TRUE);
12912b24ab6bSSebastien Roy 
12922b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(0, cred)) != 0)
12932b24ab6bSSebastien Roy 		goto done;
12942b24ab6bSSebastien Roy 
12952b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID) {
12962b24ab6bSSebastien Roy 		err = EACCES;
12972b24ab6bSSebastien Roy 		goto done;
12982b24ab6bSSebastien Roy 	}
12992b24ab6bSSebastien Roy 	if (zoneboot->ld_zoneid == GLOBAL_ZONEID) {
13002b24ab6bSSebastien Roy 		err = EINVAL;
13012b24ab6bSSebastien Roy 		goto done;
13022b24ab6bSSebastien Roy 	}
13032b24ab6bSSebastien Roy 
13042b24ab6bSSebastien Roy 	if ((err = dlmgmt_elevate_privileges()) == 0) {
13052b24ab6bSSebastien Roy 		err = dlmgmt_zone_init(zoneboot->ld_zoneid);
13062b24ab6bSSebastien Roy 		(void) dlmgmt_drop_privileges();
13072b24ab6bSSebastien Roy 	}
13082b24ab6bSSebastien Roy done:
13092b24ab6bSSebastien Roy 	dlmgmt_table_unlock();
13102b24ab6bSSebastien Roy 	retvalp->lr_err = err;
13112b24ab6bSSebastien Roy }
13122b24ab6bSSebastien Roy 
131332715170SCathy Zhou /* ARGSUSED */
13142b24ab6bSSebastien Roy static void
dlmgmt_zonehalt(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)131532715170SCathy Zhou dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
131632715170SCathy Zhou     ucred_t *cred)
13172b24ab6bSSebastien Roy {
13182b24ab6bSSebastien Roy 	int			err = 0;
13192b24ab6bSSebastien Roy 	dlmgmt_door_zonehalt_t	*zonehalt = argp;
13202b24ab6bSSebastien Roy 	dlmgmt_zonehalt_retval_t *retvalp = retp;
13212b24ab6bSSebastien Roy 
13222b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
13232b24ab6bSSebastien Roy 		if (zoneid != GLOBAL_ZONEID) {
13242b24ab6bSSebastien Roy 			err = EACCES;
13252b24ab6bSSebastien Roy 		} else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
13262b24ab6bSSebastien Roy 			err = EINVAL;
13272b24ab6bSSebastien Roy 		} else {
13282b24ab6bSSebastien Roy 			dlmgmt_table_lock(B_TRUE);
13292b24ab6bSSebastien Roy 			dlmgmt_db_fini(zonehalt->ld_zoneid);
13302b24ab6bSSebastien Roy 			dlmgmt_table_unlock();
13312b24ab6bSSebastien Roy 		}
13322b24ab6bSSebastien Roy 	}
13332b24ab6bSSebastien Roy 	retvalp->lr_err = err;
13342b24ab6bSSebastien Roy }
13352b24ab6bSSebastien Roy 
1336024b0a25Sseb static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
13372b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t),
1338024b0a25Sseb 	    sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
13392b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t),
1340024b0a25Sseb 	    sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
13412b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t),
1342024b0a25Sseb 	    sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
13432b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t),
1344024b0a25Sseb 	    sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
13452b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t),
1346024b0a25Sseb 	    sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
13472b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t),
1348024b0a25Sseb 	    sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
13492b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t),
1350024b0a25Sseb 	    sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },
13512b24ab6bSSebastien Roy 	{ DLMGMT_CMD_CREATE_LINKID, sizeof (dlmgmt_door_createid_t),
1352024b0a25Sseb 	    sizeof (dlmgmt_createid_retval_t), dlmgmt_createid },
13532b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DESTROY_LINKID, sizeof (dlmgmt_door_destroyid_t),
1354024b0a25Sseb 	    sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid },
13552b24ab6bSSebastien Roy 	{ DLMGMT_CMD_REMAP_LINKID, sizeof (dlmgmt_door_remapid_t),
1356024b0a25Sseb 	    sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid },
13572b24ab6bSSebastien Roy 	{ DLMGMT_CMD_CREATECONF, sizeof (dlmgmt_door_createconf_t),
1358024b0a25Sseb 	    sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf },
135932715170SCathy Zhou 	{ DLMGMT_CMD_OPENCONF, sizeof (dlmgmt_door_openconf_t),
136032715170SCathy Zhou 	    sizeof (dlmgmt_openconf_retval_t), dlmgmt_openconf },
13612b24ab6bSSebastien Roy 	{ DLMGMT_CMD_WRITECONF, sizeof (dlmgmt_door_writeconf_t),
1362024b0a25Sseb 	    sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf },
13632b24ab6bSSebastien Roy 	{ DLMGMT_CMD_UP_LINKID, sizeof (dlmgmt_door_upid_t),
1364024b0a25Sseb 	    sizeof (dlmgmt_upid_retval_t), dlmgmt_upid },
13652b24ab6bSSebastien Roy 	{ DLMGMT_CMD_SETATTR, sizeof (dlmgmt_door_setattr_t),
1366024b0a25Sseb 	    sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr },
13672b24ab6bSSebastien Roy 	{ DLMGMT_CMD_UNSETATTR, sizeof (dlmgmt_door_unsetattr_t),
1368024b0a25Sseb 	    sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr },
13692b24ab6bSSebastien Roy 	{ DLMGMT_CMD_REMOVECONF, sizeof (dlmgmt_door_removeconf_t),
1370024b0a25Sseb 	    sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf },
13712b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DESTROYCONF, sizeof (dlmgmt_door_destroyconf_t),
1372024b0a25Sseb 	    sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf },
13732b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETATTR, sizeof (dlmgmt_door_getattr_t),
137430890389Sartem 	    sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr },
137532715170SCathy Zhou 	{ DLMGMT_CMD_GETCONFSNAPSHOT, sizeof (dlmgmt_door_getconfsnapshot_t),
137632715170SCathy Zhou 	    sizeof (dlmgmt_getconfsnapshot_retval_t), dlmgmt_getconfsnapshot },
13772b24ab6bSSebastien Roy 	{ DLMGMT_CMD_LINKPROP_INIT, sizeof (dlmgmt_door_linkprop_init_t),
137830890389Sartem 	    sizeof (dlmgmt_linkprop_init_retval_t),
137962ee1d25SArtem Kachitchkine 	    dlmgmt_upcall_linkprop_init },
13802b24ab6bSSebastien Roy 	{ DLMGMT_CMD_SETZONEID, sizeof (dlmgmt_door_setzoneid_t),
13812b24ab6bSSebastien Roy 	    sizeof (dlmgmt_setzoneid_retval_t), dlmgmt_setzoneid },
13822b24ab6bSSebastien Roy 	{ DLMGMT_CMD_ZONEBOOT, sizeof (dlmgmt_door_zoneboot_t),
13832b24ab6bSSebastien Roy 	    sizeof (dlmgmt_zoneboot_retval_t), dlmgmt_zoneboot },
13842b24ab6bSSebastien Roy 	{ DLMGMT_CMD_ZONEHALT, sizeof (dlmgmt_door_zonehalt_t),
13852b24ab6bSSebastien Roy 	    sizeof (dlmgmt_zonehalt_retval_t), dlmgmt_zonehalt },
13862b24ab6bSSebastien Roy 	{ 0, 0, 0, NULL }
1387024b0a25Sseb };
1388024b0a25Sseb 
13892b24ab6bSSebastien Roy static dlmgmt_door_info_t *
dlmgmt_getcmdinfo(int cmd)13902b24ab6bSSebastien Roy dlmgmt_getcmdinfo(int cmd)
13912b24ab6bSSebastien Roy {
13922b24ab6bSSebastien Roy 	dlmgmt_door_info_t	*infop = i_dlmgmt_door_info_tbl;
13932b24ab6bSSebastien Roy 
13942b24ab6bSSebastien Roy 	while (infop->di_handler != NULL) {
13952b24ab6bSSebastien Roy 		if (infop->di_cmd == cmd)
13962b24ab6bSSebastien Roy 			break;
13972b24ab6bSSebastien Roy 		infop++;
13982b24ab6bSSebastien Roy 	}
13992b24ab6bSSebastien Roy 	return (infop);
14002b24ab6bSSebastien Roy }
1401d62bc4baSyz 
1402024b0a25Sseb /* ARGSUSED */
1403024b0a25Sseb void
dlmgmt_handler(void * cookie,char * argp,size_t argsz,door_desc_t * dp,uint_t n_desc)1404024b0a25Sseb dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp,
1405024b0a25Sseb     uint_t n_desc)
1406024b0a25Sseb {
14072b24ab6bSSebastien Roy 	dlmgmt_door_arg_t	*door_arg = (dlmgmt_door_arg_t *)(void *)argp;
1408024b0a25Sseb 	dlmgmt_door_info_t	*infop = NULL;
1409024b0a25Sseb 	dlmgmt_retval_t		retval;
14102b24ab6bSSebastien Roy 	ucred_t			*cred = NULL;
14112b24ab6bSSebastien Roy 	zoneid_t		zoneid;
141232715170SCathy Zhou 	void			*retvalp = NULL;
141332715170SCathy Zhou 	size_t			sz, acksz;
1414eae72b5bSSebastien Roy 	int			err = 0;
1415d62bc4baSyz 
14162b24ab6bSSebastien Roy 	infop = dlmgmt_getcmdinfo(door_arg->ld_cmd);
1417024b0a25Sseb 	if (infop == NULL || argsz != infop->di_reqsz) {
1418024b0a25Sseb 		err = EINVAL;
14192b24ab6bSSebastien Roy 		goto done;
1420d62bc4baSyz 	}
1421d62bc4baSyz 
14222b24ab6bSSebastien Roy 	if (door_ucred(&cred) != 0 || (zoneid = ucred_getzoneid(cred)) == -1) {
14232b24ab6bSSebastien Roy 		err = errno;
14242b24ab6bSSebastien Roy 		goto done;
1425d62bc4baSyz 	}
1426d62bc4baSyz 
1427024b0a25Sseb 	/*
142832715170SCathy Zhou 	 * Note that malloc() cannot be used here because door_return
142932715170SCathy Zhou 	 * never returns, and memory allocated by malloc() would get leaked.
143032715170SCathy Zhou 	 * Use alloca() instead.
1431024b0a25Sseb 	 */
143232715170SCathy Zhou 	acksz = infop->di_acksz;
143332715170SCathy Zhou 
143432715170SCathy Zhou again:
143532715170SCathy Zhou 	retvalp = alloca(acksz);
143632715170SCathy Zhou 	sz = acksz;
143732715170SCathy Zhou 	infop->di_handler(argp, retvalp, &acksz, zoneid, cred);
143832715170SCathy Zhou 	if (acksz > sz) {
143932715170SCathy Zhou 		/*
144032715170SCathy Zhou 		 * If the specified buffer size is not big enough to hold the
144132715170SCathy Zhou 		 * return value, reallocate the buffer and try to get the
144232715170SCathy Zhou 		 * result one more time.
144332715170SCathy Zhou 		 */
144432715170SCathy Zhou 		assert(((dlmgmt_retval_t *)retvalp)->lr_err == ENOSPC);
144532715170SCathy Zhou 		goto again;
144632715170SCathy Zhou 	}
1447024b0a25Sseb 
14482b24ab6bSSebastien Roy done:
14492b24ab6bSSebastien Roy 	if (cred != NULL)
14502b24ab6bSSebastien Roy 		ucred_free(cred);
14512b24ab6bSSebastien Roy 	if (err == 0) {
145232715170SCathy Zhou 		(void) door_return(retvalp, acksz, NULL, 0);
14532b24ab6bSSebastien Roy 	} else {
14542b24ab6bSSebastien Roy 		retval.lr_err = err;
14552b24ab6bSSebastien Roy 		(void) door_return((char *)&retval, sizeof (retval), NULL, 0);
14562b24ab6bSSebastien Roy 	}
1457d62bc4baSyz }
1458