Print this page
OS-3342+co
OS-722
OS-478 -- lint
OS-375-1
OS-328 dlmgmtd/mac_link_flow_add() deadlock
OS-249


   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.

  24  */
  25 
  26 /*
  27  * Main door handler functions used by dlmgmtd to process the different door
  28  * call requests. Door call requests can come from the user-land applications,
  29  * or from the kernel.
  30  *
  31  * Note on zones handling:
  32  *
  33  * There are two zoneid's associated with a link.  One is the zoneid of the
  34  * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and
  35  * the other is the zoneid of the zone where the link is currently assigned
  36  * (the "zone" link property).  The two can be different if a datalink is
  37  * created in the global zone and subsequently assigned to a non-global zone
  38  * via zonecfg or via explicitly setting the "zone" link property.
  39  *
  40  * Door clients can see links that were created in their zone, and links that
  41  * are currently assigned to their zone.  Door clients in a zone can only
  42  * modify links that were created in their zone.
  43  *
  44  * The datalink ID space is global, while each zone has its own datalink name
  45  * space.  This allows each zone to have complete freedom over the names that
  46  * they assign to links created within the zone.
  47  */
  48 
  49 #include <assert.h>
  50 #include <alloca.h>
  51 #include <errno.h>
  52 #include <priv_utils.h>
  53 #include <stdlib.h>
  54 #include <strings.h>
  55 #include <syslog.h>
  56 #include <sys/sysevent/eventdefs.h>
  57 #include <zone.h>
  58 #include <libsysevent.h>
  59 #include <libdlmgmt.h>
  60 #include <librcm.h>




  61 #include "dlmgmt_impl.h"
  62 
  63 typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
  64     ucred_t *);
  65 
  66 typedef struct dlmgmt_door_info_s {
  67         uint_t                  di_cmd;
  68         size_t                  di_reqsz;
  69         size_t                  di_acksz;
  70         dlmgmt_door_handler_t   *di_handler;
  71 } dlmgmt_door_info_t;
  72 
  73 /*
  74  * Check if the caller has the required privileges to operate on a link of the
  75  * given class.
  76  */
  77 static int
  78 dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
  79 {
  80         const priv_set_t *eset;


 362         datalink_id_t                   linkid = destroy->ld_linkid;
 363         dlmgmt_link_t                   *linkp = NULL;
 364         uint32_t                        flags, dflags = 0;
 365         int                             err = 0;
 366 
 367         flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
 368 
 369         /*
 370          * Hold the writer lock to update the link table.
 371          */
 372         dlmgmt_table_lock(B_TRUE);
 373 
 374         if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
 375                 err = ENOENT;
 376                 goto done;
 377         }
 378 
 379         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 380                 goto done;
 381 





 382         if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
 383                 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
 384                         goto done;
 385                 dflags |= DLMGMT_ACTIVE;
 386         }
 387 
 388         if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) {
 389                 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0)
 390                         goto done;
 391                 dflags |= DLMGMT_PERSIST;
 392         }
 393 
 394         err = dlmgmt_destroy_common(linkp, flags);
 395 done:
 396         if (err != 0 && dflags != 0)
 397                 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags);
 398 
 399         dlmgmt_table_unlock();
 400         retvalp->lr_err = err;
 401 }


 422         } else {
 423                 retvalp->lr_flags = linkp->ll_flags;
 424                 retvalp->lr_class = linkp->ll_class;
 425                 retvalp->lr_media = linkp->ll_media;
 426         }
 427 
 428         dlmgmt_table_unlock();
 429         retvalp->lr_err = err;
 430 }
 431 
 432 /* ARGSUSED */
 433 static void
 434 dlmgmt_getlinkid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
 435     ucred_t *cred)
 436 {
 437         dlmgmt_door_getlinkid_t *getlinkid = argp;
 438         dlmgmt_getlinkid_retval_t *retvalp = retp;
 439         dlmgmt_link_t           *linkp;
 440         int                     err = 0;
 441 




 442         /*
 443          * Hold the reader lock to access the link
 444          */
 445         dlmgmt_table_lock(B_FALSE);
 446 
 447         if ((linkp = link_by_name(getlinkid->ld_link, zoneid)) == NULL) {
 448                 /*
 449                  * The link does not exist in this zone.
 450                  */
 451                 err = ENOENT;
 452                 goto done;
 453         }
 454 
 455         retvalp->lr_linkid = linkp->ll_linkid;
 456         retvalp->lr_flags = linkp->ll_flags;
 457         retvalp->lr_class = linkp->ll_class;
 458         retvalp->lr_media = linkp->ll_media;
 459 
 460 done:
 461         dlmgmt_table_unlock();


 631         boolean_t               renamed = B_FALSE;
 632         int                     err = 0;
 633 
 634         if (!dladm_valid_linkname(remapid->ld_link)) {
 635                 retvalp->lr_err = EINVAL;
 636                 return;
 637         }
 638 
 639         /*
 640          * Hold the writer lock to update the link table.
 641          */
 642         dlmgmt_table_lock(B_TRUE);
 643         if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) {
 644                 err = ENOENT;
 645                 goto done;
 646         }
 647 
 648         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 649                 goto done;
 650 






 651         if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
 652                 err = EEXIST;
 653                 goto done;
 654         }
 655 
 656         (void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN);
 657         avl_remove(&dlmgmt_name_avl, linkp);
 658         (void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN);
 659         avl_add(&dlmgmt_name_avl, linkp);
 660         renamed = B_TRUE;
 661 
 662         if (linkp->ll_flags & DLMGMT_ACTIVE) {
 663                 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE);
 664                 if (err != 0)
 665                         goto done;
 666         }
 667         if (linkp->ll_flags & DLMGMT_PERSIST) {
 668                 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST);
 669                 if (err != 0) {
 670                         if (linkp->ll_flags & DLMGMT_ACTIVE) {


 692 dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
 693     ucred_t *cred)
 694 {
 695         dlmgmt_door_upid_t      *upid = argp;
 696         dlmgmt_upid_retval_t    *retvalp = retp;
 697         dlmgmt_link_t           *linkp;
 698         int                     err = 0;
 699 
 700         /*
 701          * Hold the writer lock to update the link table.
 702          */
 703         dlmgmt_table_lock(B_TRUE);
 704         if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) {
 705                 err = ENOENT;
 706                 goto done;
 707         }
 708 
 709         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 710                 goto done;
 711 





 712         if (linkp->ll_flags & DLMGMT_ACTIVE) {
 713                 err = EINVAL;
 714                 goto done;
 715         }
 716 
 717         if ((err = link_activate(linkp)) == 0) {
 718                 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
 719                     DLMGMT_ACTIVE);
 720         }
 721 done:
 722         dlmgmt_table_unlock();
 723         retvalp->lr_err = err;
 724 }
 725 
 726 /* ARGSUSED */
 727 static void
 728 dlmgmt_createconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
 729     ucred_t *cred)
 730 {
 731         dlmgmt_door_createconf_t *createconf = argp;


1199         datalink_id_t           linkid = setzoneid->ld_linkid;
1200         zoneid_t                oldzoneid, newzoneid;
1201         int                     err = 0;
1202 
1203         dlmgmt_table_lock(B_TRUE);
1204 
1205         /* We currently only allow changing zoneid's from the global zone. */
1206         if (zoneid != GLOBAL_ZONEID) {
1207                 err = EACCES;
1208                 goto done;
1209         }
1210 
1211         if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
1212                 err = ENOENT;
1213                 goto done;
1214         }
1215 
1216         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
1217                 goto done;
1218 





1219         /* We can only assign an active link to a zone. */
1220         if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
1221                 err = EINVAL;
1222                 goto done;
1223         }
1224 
1225         oldzoneid = linkp->ll_zoneid;
1226         newzoneid = setzoneid->ld_zoneid;
1227 
1228         if (oldzoneid == newzoneid)
1229                 goto done;
1230 
1231         /*
1232          * Before we remove the link from its current zone, make sure that
1233          * there isn't a link with the same name in the destination zone.
1234          */
1235         if (zoneid != GLOBAL_ZONEID &&
1236             link_by_name(linkp->ll_link, newzoneid) != NULL) {
1237                 err = EEXIST;
1238                 goto done;
1239         }
1240 
1241         if (oldzoneid != GLOBAL_ZONEID) {
1242                 if (zone_remove_datalink(oldzoneid, linkid) != 0) {
1243                         err = errno;
1244                         dlmgmt_log(LOG_WARNING, "unable to remove link %d from "
1245                             "zone %d: %s", linkid, oldzoneid, strerror(err));
1246                         goto done;
1247                 }
1248                 avl_remove(&dlmgmt_loan_avl, linkp);












1249                 linkp->ll_onloan = B_FALSE;
1250         }
1251         if (newzoneid != GLOBAL_ZONEID) {
1252                 if (zone_add_datalink(newzoneid, linkid) != 0) {
1253                         err = errno;
1254                         dlmgmt_log(LOG_WARNING, "unable to add link %d to zone "
1255                             "%d: %s", linkid, newzoneid, strerror(err));
1256                         (void) zone_add_datalink(oldzoneid, linkid);
1257                         goto done;
1258                 }
1259                 avl_add(&dlmgmt_loan_avl, linkp);
1260                 linkp->ll_onloan = B_TRUE;
1261         }
1262 
1263         avl_remove(&dlmgmt_name_avl, linkp);
1264         linkp->ll_zoneid = newzoneid;
1265         avl_add(&dlmgmt_name_avl, linkp);
1266 
1267 done:
1268         dlmgmt_table_unlock();
1269         retvalp->lr_err = err;
1270 }
1271 
1272 /* ARGSUSED */
1273 static void
1274 dlmgmt_zoneboot(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1275     ucred_t *cred)
1276 {
1277         int                     err;
1278         dlmgmt_door_zoneboot_t  *zoneboot = argp;
1279         dlmgmt_zoneboot_retval_t *retvalp = retp;


1292                 goto done;
1293         }
1294 
1295         if ((err = dlmgmt_elevate_privileges()) == 0) {
1296                 err = dlmgmt_zone_init(zoneboot->ld_zoneid);
1297                 (void) dlmgmt_drop_privileges();
1298         }
1299 done:
1300         dlmgmt_table_unlock();
1301         retvalp->lr_err = err;
1302 }
1303 
1304 /* ARGSUSED */
1305 static void
1306 dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1307     ucred_t *cred)
1308 {
1309         int                     err = 0;
1310         dlmgmt_door_zonehalt_t  *zonehalt = argp;
1311         dlmgmt_zonehalt_retval_t *retvalp = retp;

1312 



1313         if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
1314                 if (zoneid != GLOBAL_ZONEID) {
1315                         err = EACCES;
1316                 } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
1317                         err = EINVAL;
1318                 } else {















1319                         dlmgmt_table_lock(B_TRUE);
1320                         dlmgmt_db_fini(zonehalt->ld_zoneid);
1321                         dlmgmt_table_unlock();


1322                 }
1323         }
1324         retvalp->lr_err = err;
1325 }
1326 
1327 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
1328         { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t),
1329             sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
1330         { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t),
1331             sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
1332         { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t),
1333             sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
1334         { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t),
1335             sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
1336         { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t),
1337             sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
1338         { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t),
1339             sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
1340         { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t),
1341             sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },




   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2011, Joyent Inc. All rights reserved.
  25  */
  26 
  27 /*
  28  * Main door handler functions used by dlmgmtd to process the different door
  29  * call requests. Door call requests can come from the user-land applications,
  30  * or from the kernel.
  31  *
  32  * Note on zones handling:
  33  *
  34  * There are two zoneid's associated with a link.  One is the zoneid of the
  35  * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and
  36  * the other is the zoneid of the zone where the link is currently assigned
  37  * (the "zone" link property).  The two can be different if a datalink is
  38  * created in the global zone and subsequently assigned to a non-global zone
  39  * via zonecfg or via explicitly setting the "zone" link property.
  40  *
  41  * Door clients can see links that were created in their zone, and links that
  42  * are currently assigned to their zone.  Door clients in a zone can only
  43  * modify links that were created in their zone.
  44  *
  45  * The datalink ID space is global, while each zone has its own datalink name
  46  * space.  This allows each zone to have complete freedom over the names that
  47  * they assign to links created within the zone.
  48  */
  49 
  50 #include <assert.h>
  51 #include <alloca.h>
  52 #include <errno.h>
  53 #include <priv_utils.h>
  54 #include <stdlib.h>
  55 #include <strings.h>
  56 #include <syslog.h>
  57 #include <sys/sysevent/eventdefs.h>
  58 #include <zone.h>
  59 #include <libsysevent.h>
  60 #include <libdlmgmt.h>
  61 #include <librcm.h>
  62 #include <sys/types.h>
  63 #include <sys/stat.h>
  64 #include <fcntl.h>
  65 #include <unistd.h>
  66 #include "dlmgmt_impl.h"
  67 
  68 typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
  69     ucred_t *);
  70 
  71 typedef struct dlmgmt_door_info_s {
  72         uint_t                  di_cmd;
  73         size_t                  di_reqsz;
  74         size_t                  di_acksz;
  75         dlmgmt_door_handler_t   *di_handler;
  76 } dlmgmt_door_info_t;
  77 
  78 /*
  79  * Check if the caller has the required privileges to operate on a link of the
  80  * given class.
  81  */
  82 static int
  83 dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
  84 {
  85         const priv_set_t *eset;


 367         datalink_id_t                   linkid = destroy->ld_linkid;
 368         dlmgmt_link_t                   *linkp = NULL;
 369         uint32_t                        flags, dflags = 0;
 370         int                             err = 0;
 371 
 372         flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
 373 
 374         /*
 375          * Hold the writer lock to update the link table.
 376          */
 377         dlmgmt_table_lock(B_TRUE);
 378 
 379         if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
 380                 err = ENOENT;
 381                 goto done;
 382         }
 383 
 384         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 385                 goto done;
 386 
 387         if (linkp->ll_tomb == B_TRUE) {
 388                 err = EINPROGRESS;
 389                 goto done;
 390         }
 391 
 392         if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
 393                 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
 394                         goto done;
 395                 dflags |= DLMGMT_ACTIVE;
 396         }
 397 
 398         if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) {
 399                 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0)
 400                         goto done;
 401                 dflags |= DLMGMT_PERSIST;
 402         }
 403 
 404         err = dlmgmt_destroy_common(linkp, flags);
 405 done:
 406         if (err != 0 && dflags != 0)
 407                 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags);
 408 
 409         dlmgmt_table_unlock();
 410         retvalp->lr_err = err;
 411 }


 432         } else {
 433                 retvalp->lr_flags = linkp->ll_flags;
 434                 retvalp->lr_class = linkp->ll_class;
 435                 retvalp->lr_media = linkp->ll_media;
 436         }
 437 
 438         dlmgmt_table_unlock();
 439         retvalp->lr_err = err;
 440 }
 441 
 442 /* ARGSUSED */
 443 static void
 444 dlmgmt_getlinkid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
 445     ucred_t *cred)
 446 {
 447         dlmgmt_door_getlinkid_t *getlinkid = argp;
 448         dlmgmt_getlinkid_retval_t *retvalp = retp;
 449         dlmgmt_link_t           *linkp;
 450         int                     err = 0;
 451 
 452         /* Enable the global zone to lookup links it has given away. */
 453         if (zoneid == GLOBAL_ZONEID && getlinkid->ld_zoneid != -1)
 454                 zoneid = getlinkid->ld_zoneid;
 455 
 456         /*
 457          * Hold the reader lock to access the link
 458          */
 459         dlmgmt_table_lock(B_FALSE);
 460 
 461         if ((linkp = link_by_name(getlinkid->ld_link, zoneid)) == NULL) {
 462                 /*
 463                  * The link does not exist in this zone.
 464                  */
 465                 err = ENOENT;
 466                 goto done;
 467         }
 468 
 469         retvalp->lr_linkid = linkp->ll_linkid;
 470         retvalp->lr_flags = linkp->ll_flags;
 471         retvalp->lr_class = linkp->ll_class;
 472         retvalp->lr_media = linkp->ll_media;
 473 
 474 done:
 475         dlmgmt_table_unlock();


 645         boolean_t               renamed = B_FALSE;
 646         int                     err = 0;
 647 
 648         if (!dladm_valid_linkname(remapid->ld_link)) {
 649                 retvalp->lr_err = EINVAL;
 650                 return;
 651         }
 652 
 653         /*
 654          * Hold the writer lock to update the link table.
 655          */
 656         dlmgmt_table_lock(B_TRUE);
 657         if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) {
 658                 err = ENOENT;
 659                 goto done;
 660         }
 661 
 662         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 663                 goto done;
 664 
 665         if (linkp->ll_tomb == B_TRUE) {
 666                 err = EBUSY;
 667                 goto done;
 668         }
 669 
 670 
 671         if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
 672                 err = EEXIST;
 673                 goto done;
 674         }
 675 
 676         (void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN);
 677         avl_remove(&dlmgmt_name_avl, linkp);
 678         (void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN);
 679         avl_add(&dlmgmt_name_avl, linkp);
 680         renamed = B_TRUE;
 681 
 682         if (linkp->ll_flags & DLMGMT_ACTIVE) {
 683                 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE);
 684                 if (err != 0)
 685                         goto done;
 686         }
 687         if (linkp->ll_flags & DLMGMT_PERSIST) {
 688                 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST);
 689                 if (err != 0) {
 690                         if (linkp->ll_flags & DLMGMT_ACTIVE) {


 712 dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
 713     ucred_t *cred)
 714 {
 715         dlmgmt_door_upid_t      *upid = argp;
 716         dlmgmt_upid_retval_t    *retvalp = retp;
 717         dlmgmt_link_t           *linkp;
 718         int                     err = 0;
 719 
 720         /*
 721          * Hold the writer lock to update the link table.
 722          */
 723         dlmgmt_table_lock(B_TRUE);
 724         if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) {
 725                 err = ENOENT;
 726                 goto done;
 727         }
 728 
 729         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 730                 goto done;
 731 
 732         if (linkp->ll_tomb == B_TRUE) {
 733                 err = EBUSY;
 734                 goto done;
 735         }
 736 
 737         if (linkp->ll_flags & DLMGMT_ACTIVE) {
 738                 err = EINVAL;
 739                 goto done;
 740         }
 741 
 742         if ((err = link_activate(linkp)) == 0) {
 743                 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
 744                     DLMGMT_ACTIVE);
 745         }
 746 done:
 747         dlmgmt_table_unlock();
 748         retvalp->lr_err = err;
 749 }
 750 
 751 /* ARGSUSED */
 752 static void
 753 dlmgmt_createconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
 754     ucred_t *cred)
 755 {
 756         dlmgmt_door_createconf_t *createconf = argp;


1224         datalink_id_t           linkid = setzoneid->ld_linkid;
1225         zoneid_t                oldzoneid, newzoneid;
1226         int                     err = 0;
1227 
1228         dlmgmt_table_lock(B_TRUE);
1229 
1230         /* We currently only allow changing zoneid's from the global zone. */
1231         if (zoneid != GLOBAL_ZONEID) {
1232                 err = EACCES;
1233                 goto done;
1234         }
1235 
1236         if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
1237                 err = ENOENT;
1238                 goto done;
1239         }
1240 
1241         if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
1242                 goto done;
1243 
1244         if (linkp->ll_tomb == B_TRUE) {
1245                 err = EBUSY;
1246                 goto done;
1247         }
1248 
1249         /* We can only assign an active link to a zone. */
1250         if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
1251                 err = EINVAL;
1252                 goto done;
1253         }
1254 
1255         oldzoneid = linkp->ll_zoneid;
1256         newzoneid = setzoneid->ld_zoneid;
1257 
1258         if (oldzoneid == newzoneid)
1259                 goto done;
1260 
1261         /*
1262          * Before we remove the link from its current zone, make sure that
1263          * there isn't a link with the same name in the destination zone.
1264          */
1265         if (zoneid != GLOBAL_ZONEID &&
1266             link_by_name(linkp->ll_link, newzoneid) != NULL) {
1267                 err = EEXIST;
1268                 goto done;
1269         }
1270 
1271         if (oldzoneid != GLOBAL_ZONEID) {
1272                 if (zone_remove_datalink(oldzoneid, linkid) != 0) {
1273                         err = errno;
1274                         dlmgmt_log(LOG_WARNING, "unable to remove link %d from "
1275                             "zone %d: %s", linkid, oldzoneid, strerror(err));
1276                         goto done;
1277                 }
1278 
1279                 if (newzoneid == GLOBAL_ZONEID && linkp->ll_onloan) {
1280                         /*
1281                          * We can only reassign a loaned VNIC back to the
1282                          * global zone when the zone is shutting down, since
1283                          * otherwise the VNIC is in use by the zone and will be
1284                          * busy.  Leave the VNIC assigned to the zone so we can
1285                          * still see it and delete it when dlmgmt_zonehalt()
1286                          * runs.
1287                          */
1288                         goto done;
1289                 }
1290 
1291                 linkp->ll_onloan = B_FALSE;
1292         }
1293         if (newzoneid != GLOBAL_ZONEID) {
1294                 if (zone_add_datalink(newzoneid, linkid) != 0) {
1295                         err = errno;
1296                         dlmgmt_log(LOG_WARNING, "unable to add link %d to zone "
1297                             "%d: %s", linkid, newzoneid, strerror(err));
1298                         (void) zone_add_datalink(oldzoneid, linkid);
1299                         goto done;
1300                 }

1301                 linkp->ll_onloan = B_TRUE;
1302         }
1303 
1304         avl_remove(&dlmgmt_name_avl, linkp);
1305         linkp->ll_zoneid = newzoneid;
1306         avl_add(&dlmgmt_name_avl, linkp);
1307 
1308 done:
1309         dlmgmt_table_unlock();
1310         retvalp->lr_err = err;
1311 }
1312 
1313 /* ARGSUSED */
1314 static void
1315 dlmgmt_zoneboot(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1316     ucred_t *cred)
1317 {
1318         int                     err;
1319         dlmgmt_door_zoneboot_t  *zoneboot = argp;
1320         dlmgmt_zoneboot_retval_t *retvalp = retp;


1333                 goto done;
1334         }
1335 
1336         if ((err = dlmgmt_elevate_privileges()) == 0) {
1337                 err = dlmgmt_zone_init(zoneboot->ld_zoneid);
1338                 (void) dlmgmt_drop_privileges();
1339         }
1340 done:
1341         dlmgmt_table_unlock();
1342         retvalp->lr_err = err;
1343 }
1344 
1345 /* ARGSUSED */
1346 static void
1347 dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1348     ucred_t *cred)
1349 {
1350         int                     err = 0;
1351         dlmgmt_door_zonehalt_t  *zonehalt = argp;
1352         dlmgmt_zonehalt_retval_t *retvalp = retp;
1353         static char my_pid[10];
1354 
1355         if (my_pid[0] == NULL)
1356                 (void) snprintf(my_pid, sizeof (my_pid), "%d\n", getpid());
1357 
1358         if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
1359                 if (zoneid != GLOBAL_ZONEID) {
1360                         err = EACCES;
1361                 } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
1362                         err = EINVAL;
1363                 } else {
1364                         /*
1365                          * dls and mac don't honor the locking rules defined in
1366                          * mac. In order to try and make that case less likely
1367                          * to happen, we try to serialize some of the zone
1368                          * activity here between dlmgmtd and the brands on
1369                          * /etc/dladm/zone.lck
1370                          */
1371                         int fd;
1372 
1373                         while ((fd = open(ZONE_LOCK, O_WRONLY |
1374                             O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
1375                         (void) sleep(1);
1376                         (void) write(fd, my_pid, sizeof (my_pid));
1377                         (void) close(fd);
1378 
1379                         dlmgmt_table_lock(B_TRUE);
1380                         dlmgmt_db_fini(zonehalt->ld_zoneid);
1381                         dlmgmt_table_unlock();
1382 
1383                         (void) unlink(ZONE_LOCK);
1384                 }
1385         }
1386         retvalp->lr_err = err;
1387 }
1388 
1389 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
1390         { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t),
1391             sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
1392         { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t),
1393             sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
1394         { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t),
1395             sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
1396         { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t),
1397             sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
1398         { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t),
1399             sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
1400         { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t),
1401             sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
1402         { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t),
1403             sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },