1 /*
   2  * CDDL HEADER START
   3  *
   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 #include <assert.h>
  27 #include <ctype.h>
  28 #include <errno.h>
  29 #include <fcntl.h>
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <strings.h>
  34 #include <syslog.h>
  35 #include <zone.h>
  36 #include <sys/types.h>
  37 #include <sys/stat.h>
  38 #include <stropts.h>
  39 #include <sys/conf.h>
  40 #include <pthread.h>
  41 #include <unistd.h>
  42 #include <wait.h>
  43 #include <libcontract.h>
  44 #include <libcontract_priv.h>
  45 #include <sys/contract/process.h>
  46 #include "dlmgmt_impl.h"
  47 
  48 typedef enum dlmgmt_db_op {
  49         DLMGMT_DB_OP_WRITE,
  50         DLMGMT_DB_OP_DELETE,
  51         DLMGMT_DB_OP_READ
  52 } dlmgmt_db_op_t;
  53 
  54 typedef struct dlmgmt_db_req_s {
  55         struct dlmgmt_db_req_s  *ls_next;
  56         dlmgmt_db_op_t          ls_op;
  57         char                    ls_link[MAXLINKNAMELEN];
  58         datalink_id_t           ls_linkid;
  59         zoneid_t                ls_zoneid;
  60         uint32_t                ls_flags;       /* Either DLMGMT_ACTIVE or   */
  61                                                 /* DLMGMT_PERSIST, not both. */
  62 } dlmgmt_db_req_t;
  63 
  64 /*
  65  * List of pending db updates (e.g., because of a read-only filesystem).
  66  */
  67 static dlmgmt_db_req_t  *dlmgmt_db_req_head = NULL;
  68 static dlmgmt_db_req_t  *dlmgmt_db_req_tail = NULL;
  69 
  70 /*
  71  * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
  72  * line with an old format.  This will cause the file being read to be
  73  * re-written with the current format.
  74  */
  75 static boolean_t        rewrite_needed;
  76 
  77 static int              dlmgmt_db_update(dlmgmt_db_op_t, const char *,
  78                             dlmgmt_link_t *, uint32_t);
  79 static int              dlmgmt_process_db_req(dlmgmt_db_req_t *);
  80 static int              dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
  81 static void             *dlmgmt_db_update_thread(void *);
  82 static boolean_t        process_link_line(char *, dlmgmt_link_t *);
  83 static int              process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
  84 static int              process_db_read(dlmgmt_db_req_t *, FILE *);
  85 static void             generate_link_line(dlmgmt_link_t *, boolean_t, char *);
  86 
  87 #define BUFLEN(lim, ptr)        (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
  88 #define MAXLINELEN              1024
  89 
  90 typedef void db_walk_func_t(dlmgmt_link_t *);
  91 
  92 /*
  93  * Translator functions to go from dladm_datatype_t to character strings.
  94  * Each function takes a pointer to a buffer, the size of the buffer,
  95  * the name of the attribute, and the value to be written.  The functions
  96  * return the number of bytes written to the buffer.  If the buffer is not big
  97  * enough to hold the string representing the value, then nothing is written
  98  * and 0 is returned.
  99  */
 100 typedef size_t write_func_t(char *, size_t, char *, void *);
 101 
 102 /*
 103  * Translator functions to read from a NULL terminated string buffer into
 104  * something of the given DLADM_TYPE_*.  The functions each return the number
 105  * of bytes read from the string buffer.  If there is an error reading data
 106  * from the buffer, then 0 is returned.  It is the caller's responsibility
 107  * to free the data allocated by these functions.
 108  */
 109 typedef size_t read_func_t(char *, void **);
 110 
 111 typedef struct translator_s {
 112         const char      *type_name;
 113         write_func_t    *write_func;
 114         read_func_t     *read_func;
 115 } translator_t;
 116 
 117 /*
 118  * Translator functions, defined later but declared here so that
 119  * the translator table can be defined.
 120  */
 121 static write_func_t     write_str, write_boolean, write_uint64;
 122 static read_func_t      read_str, read_boolean, read_int64;
 123 
 124 /*
 125  * Translator table, indexed by dladm_datatype_t.
 126  */
 127 static translator_t translators[] = {
 128         { "string",     write_str,      read_str        },
 129         { "boolean",    write_boolean,  read_boolean    },
 130         { "int",        write_uint64,   read_int64      }
 131 };
 132 
 133 static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
 134 
 135 #define LINK_PROPERTY_DELIMINATOR       ";"
 136 #define LINK_PROPERTY_TYPE_VALUE_SEP    ","
 137 #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
 138                                     strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
 139                                     strlen(LINK_PROPERTY_DELIMINATOR) +\
 140                                     strlen((n)))
 141 #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
 142             (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
 143             translators[(type)].type_name, \
 144             LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
 145 
 146 /*
 147  * Name of the cache file to keep the active <link name, linkid> mapping
 148  */
 149 char    cachefile[MAXPATHLEN];
 150 
 151 #define DLMGMT_PERSISTENT_DB_PATH       "/etc/dladm/datalink.conf"
 152 #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent)    \
 153         (void) snprintf((buffer), MAXPATHLEN, "%s", \
 154         (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
 155 
 156 typedef struct zopen_arg {
 157         const char      *zopen_modestr;
 158         int             *zopen_pipe;
 159         int             zopen_fd;
 160 } zopen_arg_t;
 161 
 162 typedef struct zrename_arg {
 163         const char      *zrename_newname;
 164 } zrename_arg_t;
 165 
 166 typedef union zfoparg {
 167         zopen_arg_t     zfop_openarg;
 168         zrename_arg_t   zfop_renamearg;
 169 } zfoparg_t;
 170 
 171 typedef struct zfcbarg {
 172         boolean_t       zfarg_inglobalzone; /* is callback in global zone? */
 173         zoneid_t        zfarg_finglobalzone; /* is file in global zone? */
 174         const char      *zfarg_filename;
 175         zfoparg_t       *zfarg_oparg;
 176 } zfarg_t;
 177 #define zfarg_openarg   zfarg_oparg->zfop_openarg
 178 #define zfarg_renamearg zfarg_oparg->zfop_renamearg
 179 
 180 /* zone file callback */
 181 typedef int zfcb_t(zfarg_t *);
 182 
 183 /*
 184  * Execute an operation on filename relative to zoneid's zone root.  If the
 185  * file is in the global zone, then the zfcb() callback will simply be called
 186  * directly.  If the file is in a non-global zone, then zfcb() will be called
 187  * both from the global zone's context, and from the non-global zone's context
 188  * (from a fork()'ed child that has entered the non-global zone).  This is
 189  * done to allow the callback to communicate with itself if needed (e.g. to
 190  * pass back the file descriptor of an opened file).
 191  */
 192 static int
 193 dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb,
 194     zfoparg_t *zfoparg)
 195 {
 196         int             ctfd;
 197         int             err;
 198         pid_t           childpid;
 199         siginfo_t       info;
 200         zfarg_t         zfarg;
 201         ctid_t          ct;
 202 
 203         if (zoneid != GLOBAL_ZONEID) {
 204                 /*
 205                  * We need to access a file that isn't in the global zone.
 206                  * Accessing non-global zone files from the global zone is
 207                  * unsafe (due to symlink attacks), we'll need to fork a child
 208                  * that enters the zone in question and executes the callback
 209                  * that will operate on the file.
 210                  *
 211                  * Before we proceed with this zone tango, we need to create a
 212                  * new process contract for the child, as required by
 213                  * zone_enter().
 214                  */
 215                 errno = 0;
 216                 ctfd = open64("/system/contract/process/template", O_RDWR);
 217                 if (ctfd == -1)
 218                         return (errno);
 219                 if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 ||
 220                     (err = ct_tmpl_set_informative(ctfd, 0)) != 0 ||
 221                     (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 ||
 222                     (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 ||
 223                     (err = ct_tmpl_activate(ctfd)) != 0) {
 224                         (void) close(ctfd);
 225                         return (err);
 226                 }
 227                 childpid = fork();
 228                 switch (childpid) {
 229                 case -1:
 230                         (void) ct_tmpl_clear(ctfd);
 231                         (void) close(ctfd);
 232                         return (err);
 233                 case 0:
 234                         (void) ct_tmpl_clear(ctfd);
 235                         (void) close(ctfd);
 236                         /*
 237                          * Elevate our privileges as zone_enter() requires all
 238                          * privileges.
 239                          */
 240                         if ((err = dlmgmt_elevate_privileges()) != 0)
 241                                 _exit(err);
 242                         if (zone_enter(zoneid) == -1)
 243                                 _exit(errno);
 244                         if ((err = dlmgmt_drop_privileges()) != 0)
 245                                 _exit(err);
 246                         break;
 247                 default:
 248                         if (contract_latest(&ct) == -1)
 249                                 ct = -1;
 250                         (void) ct_tmpl_clear(ctfd);
 251                         (void) close(ctfd);
 252                         if (waitid(P_PID, childpid, &info, WEXITED) == -1) {
 253                                 (void) contract_abandon_id(ct);
 254                                 return (errno);
 255                         }
 256                         (void) contract_abandon_id(ct);
 257                         if (info.si_status != 0)
 258                                 return (info.si_status);
 259                 }
 260         }
 261 
 262         zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0);
 263         zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID);
 264         zfarg.zfarg_filename = filename;
 265         zfarg.zfarg_oparg = zfoparg;
 266         err = zfcb(&zfarg);
 267         if (!zfarg.zfarg_inglobalzone)
 268                 _exit(err);
 269         return (err);
 270 }
 271 
 272 static int
 273 dlmgmt_zopen_cb(zfarg_t *zfarg)
 274 {
 275         struct strrecvfd recvfd;
 276         boolean_t       newfile = B_FALSE;
 277         boolean_t       inglobalzone = zfarg->zfarg_inglobalzone;
 278         zoneid_t        finglobalzone = zfarg->zfarg_finglobalzone;
 279         const char      *filename = zfarg->zfarg_filename;
 280         const char      *modestr = zfarg->zfarg_openarg.zopen_modestr;
 281         int             *p = zfarg->zfarg_openarg.zopen_pipe;
 282         struct stat     statbuf;
 283         int             oflags;
 284         mode_t          mode;
 285         int             fd = -1;
 286         int             err;
 287 
 288         /* We only ever open a file for reading or writing, not both. */
 289         oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
 290         mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 291 
 292         /* Open the file if we're in the same zone as the file. */
 293         if (inglobalzone == finglobalzone) {
 294                 /*
 295                  * First determine if we will be creating the file as part of
 296                  * opening it.  If so, then we'll need to ensure that it has
 297                  * the proper ownership after having opened it.
 298                  */
 299                 if (oflags & O_CREAT) {
 300                         if (stat(filename, &statbuf) == -1) {
 301                                 if (errno == ENOENT)
 302                                         newfile = B_TRUE;
 303                                 else
 304                                         return (errno);
 305                         }
 306                 }
 307                 if ((fd = open(filename, oflags, mode)) == -1)
 308                         return (errno);
 309                 if (newfile) {
 310                         if (chown(filename, UID_DLADM, GID_NETADM) == -1) {
 311                                 err = errno;
 312                                 (void) close(fd);
 313                                 return (err);
 314                         }
 315                 }
 316         }
 317 
 318         /*
 319          * If we're not in the global zone, send the file-descriptor back to
 320          * our parent in the global zone.
 321          */
 322         if (!inglobalzone) {
 323                 assert(!finglobalzone);
 324                 assert(fd != -1);
 325                 return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0);
 326         }
 327 
 328         /*
 329          * At this point, we know we're in the global zone.  If the file was
 330          * in a non-global zone, receive the file-descriptor from our child in
 331          * the non-global zone.
 332          */
 333         if (!finglobalzone) {
 334                 if (ioctl(p[0], I_RECVFD, &recvfd) == -1)
 335                         return (errno);
 336                 fd = recvfd.fd;
 337         }
 338 
 339         zfarg->zfarg_openarg.zopen_fd = fd;
 340         return (0);
 341 }
 342 
 343 static int
 344 dlmgmt_zunlink_cb(zfarg_t *zfarg)
 345 {
 346         if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
 347                 return (0);
 348         return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno);
 349 }
 350 
 351 static int
 352 dlmgmt_zrename_cb(zfarg_t *zfarg)
 353 {
 354         if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
 355                 return (0);
 356         return (rename(zfarg->zfarg_filename,
 357             zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno);
 358 }
 359 
 360 /*
 361  * Same as fopen(3C), except that it opens the file relative to zoneid's zone
 362  * root.
 363  */
 364 static FILE *
 365 dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid,
 366     int *err)
 367 {
 368         int             p[2];
 369         zfoparg_t       zfoparg;
 370         FILE            *fp = NULL;
 371 
 372         if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) {
 373                 *err = errno;
 374                 return (NULL);
 375         }
 376 
 377         zfoparg.zfop_openarg.zopen_modestr = modestr;
 378         zfoparg.zfop_openarg.zopen_pipe = p;
 379         *err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg);
 380         if (zoneid != GLOBAL_ZONEID) {
 381                 (void) close(p[0]);
 382                 (void) close(p[1]);
 383         }
 384         if (*err == 0) {
 385                 fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr);
 386                 if (fp == NULL) {
 387                         *err = errno;
 388                         (void) close(zfoparg.zfop_openarg.zopen_fd);
 389                 }
 390         }
 391         return (fp);
 392 }
 393 
 394 /*
 395  * Same as rename(2), except that old and new are relative to zoneid's zone
 396  * root.
 397  */
 398 static int
 399 dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid)
 400 {
 401         zfoparg_t zfoparg;
 402 
 403         zfoparg.zfop_renamearg.zrename_newname = new;
 404         return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg));
 405 }
 406 
 407 /*
 408  * Same as unlink(2), except that filename is relative to zoneid's zone root.
 409  */
 410 static int
 411 dlmgmt_zunlink(const char *filename, zoneid_t zoneid)
 412 {
 413         return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL));
 414 }
 415 
 416 static size_t
 417 write_str(char *buffer, size_t buffer_length, char *name, void *value)
 418 {
 419         char    *ptr = value;
 420         size_t  data_length = strnlen(ptr, buffer_length);
 421 
 422         /*
 423          * Strings are assumed to be NULL terminated.  In order to fit in
 424          * the buffer, the string's length must be less then buffer_length.
 425          * If the value is empty, there's no point in writing it, in fact,
 426          * we shouldn't even see that case.
 427          */
 428         if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
 429             buffer_length || data_length == 0)
 430                 return (0);
 431 
 432         /*
 433          * Since we know the string will fit in the buffer, snprintf will
 434          * always return less than buffer_length, so we can just return
 435          * whatever snprintf returns.
 436          */
 437         return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
 438             name, DLADM_TYPE_STR, ptr));
 439 }
 440 
 441 static size_t
 442 write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
 443 {
 444         boolean_t       *ptr = value;
 445 
 446         /*
 447          * Booleans are either zero or one, so we only need room for two
 448          * characters in the buffer.
 449          */
 450         if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
 451                 return (0);
 452 
 453         return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
 454             name, DLADM_TYPE_BOOLEAN, *ptr));
 455 }
 456 
 457 static size_t
 458 write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
 459 {
 460         uint64_t        *ptr = value;
 461 
 462         /*
 463          * Limit checking for uint64_t is a little trickier.
 464          */
 465         if (snprintf(NULL, 0, "%lld", *ptr)  +
 466             BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
 467                 return (0);
 468 
 469         return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
 470             name, DLADM_TYPE_UINT64, *ptr));
 471 }
 472 
 473 static size_t
 474 read_str(char *buffer, void **value)
 475 {
 476         char            *ptr = calloc(MAXLINKATTRVALLEN, sizeof (char));
 477         ssize_t         len;
 478 
 479         if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN))
 480             >= MAXLINKATTRVALLEN) {
 481                 free(ptr);
 482                 return (0);
 483         }
 484 
 485         *(char **)value = ptr;
 486 
 487         /* Account for NULL terminator */
 488         return (len + 1);
 489 }
 490 
 491 static size_t
 492 read_boolean(char *buffer, void **value)
 493 {
 494         boolean_t       *ptr = calloc(1, sizeof (boolean_t));
 495 
 496         if (ptr == NULL)
 497                 return (0);
 498 
 499         *ptr = atoi(buffer);
 500         *(boolean_t **)value = ptr;
 501 
 502         return (sizeof (boolean_t));
 503 }
 504 
 505 static size_t
 506 read_int64(char *buffer, void **value)
 507 {
 508         int64_t *ptr = calloc(1, sizeof (int64_t));
 509 
 510         if (ptr == NULL)
 511                 return (0);
 512 
 513         *ptr = (int64_t)atoll(buffer);
 514         *(int64_t **)value = ptr;
 515 
 516         return (sizeof (int64_t));
 517 }
 518 
 519 static dlmgmt_db_req_t *
 520 dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname,
 521     datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err)
 522 {
 523         dlmgmt_db_req_t *req;
 524 
 525         if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) {
 526                 *err = errno;
 527         } else {
 528                 req->ls_op = op;
 529                 if (linkname != NULL)
 530                         (void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN);
 531                 req->ls_linkid = linkid;
 532                 req->ls_zoneid = zoneid;
 533                 req->ls_flags = flags;
 534         }
 535         return (req);
 536 }
 537 
 538 /*
 539  * Update the db entry with name "entryname" using information from "linkp".
 540  */
 541 static int
 542 dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
 543     uint32_t flags)
 544 {
 545         dlmgmt_db_req_t *req;
 546         int             err;
 547 
 548         /* It is either a persistent request or an active request, not both. */
 549         assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
 550 
 551         if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
 552             linkp->ll_zoneid, flags, &err)) == NULL)
 553                 return (err);
 554 
 555         /*
 556          * If the return error is EINPROGRESS, this request is handled
 557          * asynchronously; return success.
 558          */
 559         err = dlmgmt_process_db_req(req);
 560         if (err != EINPROGRESS)
 561                 free(req);
 562         else
 563                 err = 0;
 564         return (err);
 565 }
 566 
 567 #define DLMGMT_DB_OP_STR(op)                                    \
 568         (((op) == DLMGMT_DB_OP_READ) ? "read" :                 \
 569         (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
 570 
 571 #define DLMGMT_DB_CONF_STR(flag)                                \
 572         (((flag) == DLMGMT_ACTIVE) ? "active" :                 \
 573         (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
 574 
 575 static int
 576 dlmgmt_process_db_req(dlmgmt_db_req_t *req)
 577 {
 578         pthread_t       tid;
 579         boolean_t       writeop;
 580         int             err;
 581 
 582         /*
 583          * If there are already pending "write" requests, queue this request in
 584          * the pending list.  Note that this function is called while the
 585          * dlmgmt_rw_lock is held, so it is safe to access the global variables.
 586          */
 587         writeop = (req->ls_op != DLMGMT_DB_OP_READ);
 588         if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
 589             (dlmgmt_db_req_head != NULL)) {
 590                 dlmgmt_db_req_tail->ls_next = req;
 591                 dlmgmt_db_req_tail = req;
 592                 return (EINPROGRESS);
 593         }
 594 
 595         err = dlmgmt_process_db_onereq(req, writeop);
 596         if (err != EINPROGRESS && err != 0 && err != ENOENT) {
 597                 /*
 598                  * Log the error unless the request processing is still in
 599                  * progress or if the configuration file hasn't been created
 600                  * yet (ENOENT).
 601                  */
 602                 dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
 603                     "operation on %s configuration failed: %s",
 604                     DLMGMT_DB_OP_STR(req->ls_op),
 605                     DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
 606         }
 607 
 608         if (err == EINPROGRESS) {
 609                 assert(req->ls_flags == DLMGMT_PERSIST);
 610                 assert(writeop && dlmgmt_db_req_head == NULL);
 611                 dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
 612                 err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
 613                 if (err == 0)
 614                         return (EINPROGRESS);
 615         }
 616         return (err);
 617 }
 618 
 619 static int
 620 dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
 621 {
 622         int     err = 0;
 623         FILE    *fp, *nfp = NULL;
 624         char    file[MAXPATHLEN];
 625         char    newfile[MAXPATHLEN];
 626 
 627         DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
 628         fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err);
 629         /*
 630          * Note that it is not an error if the file doesn't exist.  If we're
 631          * reading, we treat this case the same way as an empty file.  If
 632          * we're writing, the file will be created when we open the file for
 633          * writing below.
 634          */
 635         if (fp == NULL && !writeop)
 636                 return (err);
 637 
 638         if (writeop) {
 639                 (void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
 640                 nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err);
 641                 if (nfp == NULL) {
 642                         /*
 643                          * EROFS can happen at boot when the file system is
 644                          * read-only.  Return EINPROGRESS so that the caller
 645                          * can add this request to the pending request list
 646                          * and start a retry thread.
 647                          */
 648                         err = (errno == EROFS ? EINPROGRESS : errno);
 649                         goto done;
 650                 }
 651         }
 652         if (writeop) {
 653                 if ((err = process_db_write(req, fp, nfp)) == 0)
 654                         err = dlmgmt_zrename(newfile, file, req->ls_zoneid);
 655         } else {
 656                 err = process_db_read(req, fp);
 657         }
 658 
 659 done:
 660         if (nfp != NULL) {
 661                 (void) fclose(nfp);
 662                 if (err != 0)
 663                         (void) dlmgmt_zunlink(newfile, req->ls_zoneid);
 664         }
 665         (void) fclose(fp);
 666         return (err);
 667 }
 668 
 669 /*ARGSUSED*/
 670 static void *
 671 dlmgmt_db_update_thread(void *arg)
 672 {
 673         dlmgmt_db_req_t *req;
 674 
 675         dlmgmt_table_lock(B_TRUE);
 676 
 677         assert(dlmgmt_db_req_head != NULL);
 678         while ((req = dlmgmt_db_req_head) != NULL) {
 679                 assert(req->ls_flags == DLMGMT_PERSIST);
 680                 if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) {
 681                         /*
 682                          * The filesystem is still read only. Go to sleep and
 683                          * try again.
 684                          */
 685                         dlmgmt_table_unlock();
 686                         (void) sleep(5);
 687                         dlmgmt_table_lock(B_TRUE);
 688                         continue;
 689                 }
 690 
 691                 /*
 692                  * The filesystem is no longer read only. Continue processing
 693                  * and remove the request from the pending list.
 694                  */
 695                 dlmgmt_db_req_head = req->ls_next;
 696                 if (dlmgmt_db_req_tail == req) {
 697                         assert(dlmgmt_db_req_head == NULL);
 698                         dlmgmt_db_req_tail = NULL;
 699                 }
 700                 free(req);
 701         }
 702 
 703         dlmgmt_table_unlock();
 704         return (NULL);
 705 }
 706 
 707 static int
 708 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
 709 {
 710         boolean_t               found_type = B_FALSE;
 711         dladm_datatype_t        type = DLADM_TYPE_STR;
 712         int                     i, len;
 713         char                    *curr;
 714         char                    attr_name[MAXLINKATTRLEN];
 715         size_t                  attr_buf_len = 0;
 716         void                    *attr_buf = NULL;
 717 
 718         curr = buf;
 719         len = strlen(buf);
 720         attr_name[0] = '\0';
 721         for (i = 0; i < len; i++) {
 722                 char            c = buf[i];
 723                 boolean_t       match = (c == '=' ||
 724                     (c == ',' && !found_type) || c == ';');
 725 
 726                 /*
 727                  * Move to the next character if there is no match and
 728                  * if we have not reached the last character.
 729                  */
 730                 if (!match && i != len - 1)
 731                         continue;
 732 
 733                 if (match) {
 734                         /*
 735                          * NUL-terminate the string pointed to by 'curr'.
 736                          */
 737                         buf[i] = '\0';
 738                         if (*curr == '\0')
 739                                 goto parse_fail;
 740                 }
 741 
 742                 if (attr_name[0] != '\0' && found_type) {
 743                         /*
 744                          * We get here after we have processed the "<prop>="
 745                          * pattern. The pattern we are now interested in is
 746                          * "<val>;".
 747                          */
 748                         if (c == '=')
 749                                 goto parse_fail;
 750 
 751                         if (strcmp(attr_name, "linkid") == 0) {
 752                                 if (read_int64(curr, &attr_buf) == 0)
 753                                         goto parse_fail;
 754                                 linkp->ll_linkid =
 755                                     (datalink_class_t)*(int64_t *)attr_buf;
 756                         } else if (strcmp(attr_name, "name") == 0) {
 757                                 if (read_str(curr, &attr_buf) == 0)
 758                                         goto parse_fail;
 759                                 (void) snprintf(linkp->ll_link,
 760                                     MAXLINKNAMELEN, "%s", attr_buf);
 761                         } else if (strcmp(attr_name, "class") == 0) {
 762                                 if (read_int64(curr, &attr_buf) == 0)
 763                                         goto parse_fail;
 764                                 linkp->ll_class =
 765                                     (datalink_class_t)*(int64_t *)attr_buf;
 766                         } else if (strcmp(attr_name, "media") == 0) {
 767                                 if (read_int64(curr, &attr_buf) == 0)
 768                                         goto parse_fail;
 769                                 linkp->ll_media =
 770                                     (uint32_t)*(int64_t *)attr_buf;
 771                         } else {
 772                                 attr_buf_len = translators[type].read_func(curr,
 773                                     &attr_buf);
 774                                 if (attr_buf_len == 0)
 775                                         goto parse_fail;
 776 
 777                                 if (linkattr_set(&(linkp->ll_head), attr_name,
 778                                     attr_buf, attr_buf_len, type) != 0) {
 779                                         free(attr_buf);
 780                                         goto parse_fail;
 781                                 }
 782                         }
 783 
 784                         free(attr_buf);
 785                         attr_name[0] = '\0';
 786                         found_type = B_FALSE;
 787                 } else if (attr_name[0] != '\0') {
 788                         /*
 789                          * Non-zero length attr_name and found_type of false
 790                          * indicates that we have not found the type for this
 791                          * attribute.  The pattern now is "<type>,<val>;", we
 792                          * want the <type> part of the pattern.
 793                          */
 794                         for (type = 0; type < ntranslators; type++) {
 795                                 if (strcmp(curr,
 796                                     translators[type].type_name) == 0) {
 797                                         found_type = B_TRUE;
 798                                         break;
 799                                 }
 800                         }
 801 
 802                         if (!found_type)
 803                                 goto parse_fail;
 804                 } else {
 805                         /*
 806                          * A zero length attr_name indicates we are looking
 807                          * at the beginning of a link attribute.
 808                          */
 809                         if (c != '=')
 810                                 goto parse_fail;
 811 
 812                         (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
 813                 }
 814                 curr = buf + i + 1;
 815         }
 816 
 817         /* Correct any erroneous IPTUN datalink class constant in the file */
 818         if (linkp->ll_class == 0x60) {
 819                 linkp->ll_class = DATALINK_CLASS_IPTUN;
 820                 rewrite_needed = B_TRUE;
 821         }
 822 
 823         return (0);
 824 
 825 parse_fail:
 826         /*
 827          * Free linkp->ll_head (link attribute list)
 828          */
 829         linkattr_destroy(linkp);
 830         return (-1);
 831 }
 832 
 833 static boolean_t
 834 process_link_line(char *buf, dlmgmt_link_t *linkp)
 835 {
 836         int     i, len, llen;
 837         char    *str, *lasts;
 838         char    tmpbuf[MAXLINELEN];
 839 
 840         bzero(linkp, sizeof (*linkp));
 841         linkp->ll_linkid = DATALINK_INVALID_LINKID;
 842 
 843         /*
 844          * Use a copy of buf for parsing so that we can do whatever we want.
 845          */
 846         (void) strlcpy(tmpbuf, buf, MAXLINELEN);
 847 
 848         /*
 849          * Skip leading spaces, blank lines, and comments.
 850          */
 851         len = strlen(tmpbuf);
 852         for (i = 0; i < len; i++) {
 853                 if (!isspace(tmpbuf[i]))
 854                         break;
 855         }
 856         if (i == len || tmpbuf[i] == '#')
 857                 return (B_TRUE);
 858 
 859         str = tmpbuf + i;
 860         /*
 861          * Find the link name and assign it to the link structure.
 862          */
 863         if (strtok_r(str, " \n\t", &lasts) == NULL)
 864                 goto fail;
 865 
 866         llen = strlen(str);
 867         /*
 868          * Note that a previous version of the persistent datalink.conf file
 869          * stored the linkid as the first field.  In that case, the name will
 870          * be obtained through parse_linkprops from a property with the format
 871          * "name=<linkname>".  If we encounter such a format, we set
 872          * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
 873          * the new format after it's done reading in the data.
 874          */
 875         if (isdigit(str[0])) {
 876                 linkp->ll_linkid = atoi(str);
 877                 rewrite_needed = B_TRUE;
 878         } else {
 879                 if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >=
 880                     sizeof (linkp->ll_link))
 881                         goto fail;
 882         }
 883 
 884         str += llen + 1;
 885         if (str >= tmpbuf + len)
 886                 goto fail;
 887 
 888         /*
 889          * Now find the list of link properties.
 890          */
 891         if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
 892                 goto fail;
 893 
 894         if (parse_linkprops(str, linkp) < 0)
 895                 goto fail;
 896 
 897         return (B_TRUE);
 898 
 899 fail:
 900         /*
 901          * Delete corrupted line.
 902          */
 903         buf[0] = '\0';
 904         return (B_FALSE);
 905 }
 906 
 907 /*
 908  * Find any properties in linkp that refer to "old", and rename to "new".
 909  * Return B_TRUE if any renaming occurred.
 910  */
 911 static int
 912 dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new,
 913     boolean_t *renamed)
 914 {
 915         dlmgmt_linkattr_t       *attrp;
 916         char                    *newval = NULL, *pname;
 917         char                    valcp[MAXLINKATTRVALLEN];
 918         size_t                  newsize;
 919 
 920         *renamed = B_FALSE;
 921 
 922         if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL ||
 923             (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) {
 924                 if (strcmp(old, (char *)attrp->lp_val) == 0) {
 925                         newsize = strlen(new) + 1;
 926                         if ((newval = malloc(newsize)) == NULL)
 927                                 return (errno);
 928                         (void) strcpy(newval, new);
 929                         free(attrp->lp_val);
 930                         attrp->lp_val = newval;
 931                         attrp->lp_sz = newsize;
 932                         *renamed = B_TRUE;
 933                 }
 934                 return (0);
 935         }
 936 
 937         if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL)
 938                 return (0);
 939 
 940         /* <linkname>:[<linkname>:]... */
 941         if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL)
 942                 return (errno);
 943 
 944         bcopy(attrp->lp_val, valcp, sizeof (valcp));
 945         pname = strtok(valcp, ":");
 946         while (pname != NULL) {
 947                 if (strcmp(pname, old) == 0) {
 948                         (void) strcat(newval, new);
 949                         *renamed = B_TRUE;
 950                 } else {
 951                         (void) strcat(newval, pname);
 952                 }
 953                 (void) strcat(newval, ":");
 954                 pname = strtok(NULL, ":");
 955         }
 956         if (*renamed) {
 957                 free(attrp->lp_val);
 958                 attrp->lp_val = newval;
 959                 attrp->lp_sz = strlen(newval) + 1;
 960         } else {
 961                 free(newval);
 962         }
 963         return (0);
 964 }
 965 
 966 static int
 967 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
 968 {
 969         boolean_t               done = B_FALSE;
 970         int                     err = 0;
 971         dlmgmt_link_t           link_in_file, *linkp = NULL, *dblinkp;
 972         boolean_t               persist = (req->ls_flags == DLMGMT_PERSIST);
 973         boolean_t               writeall, rename, attr_renamed;
 974         char                    buf[MAXLINELEN];
 975 
 976         writeall = (req->ls_linkid == DATALINK_ALL_LINKID);
 977 
 978         if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) {
 979                 /*
 980                  * find the link in the avl tree with the given linkid.
 981                  */
 982                 linkp = link_by_id(req->ls_linkid, req->ls_zoneid);
 983                 if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
 984                         /*
 985                          * This link has already been changed. This could
 986                          * happen if the request is pending because of
 987                          * read-only file-system. If so, we are done.
 988                          */
 989                         return (0);
 990                 }
 991                 /*
 992                  * In the case of a rename, linkp's name has been updated to
 993                  * the new name, and req->ls_link is the old link name.
 994                  */
 995                 rename = (strcmp(req->ls_link, linkp->ll_link) != 0);
 996         }
 997 
 998         /*
 999          * fp can be NULL if the file didn't initially exist and we're
1000          * creating it as part of this write operation.
1001          */
1002         if (fp == NULL)
1003                 goto write;
1004 
1005         while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
1006             process_link_line(buf, &link_in_file)) {
1007                 /*
1008                  * Only the link name is needed. Free the memory allocated for
1009                  * the link attributes list of link_in_file.
1010                  */
1011                 linkattr_destroy(&link_in_file);
1012 
1013                 if (link_in_file.ll_link[0] == '\0' || done) {
1014                         /*
1015                          * this is a comment line or we are done updating the
1016                          * line for the specified link, write the rest of
1017                          * lines out.
1018                          */
1019                         if (fputs(buf, nfp) == EOF)
1020                                 err = errno;
1021                         continue;
1022                 }
1023 
1024                 switch (req->ls_op) {
1025                 case DLMGMT_DB_OP_WRITE:
1026                         /*
1027                          * For write operations, we generate a new output line
1028                          * if we're either writing all links (writeall) or if
1029                          * the name of the link in the file matches the one
1030                          * we're looking for.  Otherwise, we write out the
1031                          * buffer as-is.
1032                          *
1033                          * If we're doing a rename operation, ensure that any
1034                          * references to the link being renamed in link
1035                          * properties are also updated before we write
1036                          * anything.
1037                          */
1038                         if (writeall) {
1039                                 linkp = link_by_name(link_in_file.ll_link,
1040                                     req->ls_zoneid);
1041                         }
1042                         if (writeall || strcmp(req->ls_link,
1043                             link_in_file.ll_link) == 0) {
1044                                 generate_link_line(linkp, persist, buf);
1045                                 if (!writeall && !rename)
1046                                         done = B_TRUE;
1047                         } else if (rename && persist) {
1048                                 dblinkp = link_by_name(link_in_file.ll_link,
1049                                     req->ls_zoneid);
1050                                 err = dlmgmt_attr_rename(dblinkp, req->ls_link,
1051                                     linkp->ll_link, &attr_renamed);
1052                                 if (err != 0)
1053                                         break;
1054                                 if (attr_renamed) {
1055                                         generate_link_line(dblinkp, persist,
1056                                             buf);
1057                                 }
1058                         }
1059                         if (fputs(buf, nfp) == EOF)
1060                                 err = errno;
1061                         break;
1062                 case DLMGMT_DB_OP_DELETE:
1063                         /*
1064                          * Delete is simple.  If buf does not represent the
1065                          * link we're deleting, write it out.
1066                          */
1067                         if (strcmp(req->ls_link, link_in_file.ll_link) != 0) {
1068                                 if (fputs(buf, nfp) == EOF)
1069                                         err = errno;
1070                         } else {
1071                                 done = B_TRUE;
1072                         }
1073                         break;
1074                 case DLMGMT_DB_OP_READ:
1075                 default:
1076                         err = EINVAL;
1077                         break;
1078                 }
1079         }
1080 
1081 write:
1082         /*
1083          * If we get to the end of the file and have not seen what linkid
1084          * points to, write it out then.
1085          */
1086         if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) {
1087                 generate_link_line(linkp, persist, buf);
1088                 done = B_TRUE;
1089                 if (fputs(buf, nfp) == EOF)
1090                         err = errno;
1091         }
1092 
1093         return (err);
1094 }
1095 
1096 static int
1097 process_db_read(dlmgmt_db_req_t *req, FILE *fp)
1098 {
1099         avl_index_t     name_where, id_where;
1100         dlmgmt_link_t   link_in_file, *newlink, *link_in_db;
1101         char            buf[MAXLINELEN];
1102         int             err = 0;
1103 
1104         /*
1105          * This loop processes each line of the configuration file.
1106          */
1107         while (fgets(buf, MAXLINELEN, fp) != NULL) {
1108                 if (!process_link_line(buf, &link_in_file)) {
1109                         err = EINVAL;
1110                         break;
1111                 }
1112 
1113                 /*
1114                  * Skip the comment line.
1115                  */
1116                 if (link_in_file.ll_link[0] == '\0') {
1117                         linkattr_destroy(&link_in_file);
1118                         continue;
1119                 }
1120 
1121                 if ((req->ls_flags & DLMGMT_ACTIVE) &&
1122                     link_in_file.ll_linkid == DATALINK_INVALID_LINKID) {
1123                         linkattr_destroy(&link_in_file);
1124                         continue;
1125                 }
1126 
1127                 link_in_file.ll_zoneid = req->ls_zoneid;
1128                 link_in_db = link_by_name(link_in_file.ll_link,
1129                     link_in_file.ll_zoneid);
1130                 if (link_in_db != NULL) {
1131                         /*
1132                          * If the link in the database already has the flag
1133                          * for this request set, then the entry is a
1134                          * duplicate.  If it's not a duplicate, then simply
1135                          * turn on the appropriate flag on the existing link.
1136                          */
1137                         if (link_in_db->ll_flags & req->ls_flags) {
1138                                 dlmgmt_log(LOG_WARNING, "Duplicate links "
1139                                     "in the repository: %s",
1140                                     link_in_file.ll_link);
1141                                 linkattr_destroy(&link_in_file);
1142                         } else {
1143                                 if (req->ls_flags & DLMGMT_PERSIST) {
1144                                         /*
1145                                          * Save the newly read properties into
1146                                          * the existing link.
1147                                          */
1148                                         assert(link_in_db->ll_head == NULL);
1149                                         link_in_db->ll_head =
1150                                             link_in_file.ll_head;
1151                                 } else {
1152                                         linkattr_destroy(&link_in_file);
1153                                 }
1154                                 link_in_db->ll_flags |= req->ls_flags;
1155                         }
1156                 } else {
1157                         /*
1158                          * This is a new link.  Allocate a new dlmgmt_link_t
1159                          * and add it to the trees.
1160                          */
1161                         newlink = calloc(1, sizeof (*newlink));
1162                         if (newlink == NULL) {
1163                                 dlmgmt_log(LOG_WARNING, "Unable to allocate "
1164                                     "memory to create new link %s",
1165                                     link_in_file.ll_link);
1166                                 linkattr_destroy(&link_in_file);
1167                                 continue;
1168                         }
1169                         bcopy(&link_in_file, newlink, sizeof (*newlink));
1170 
1171                         if (newlink->ll_linkid == DATALINK_INVALID_LINKID)
1172                                 newlink->ll_linkid = dlmgmt_nextlinkid;
1173                         if (avl_find(&dlmgmt_id_avl, newlink, &id_where) !=
1174                             NULL) {
1175                                 dlmgmt_log(LOG_WARNING, "Link ID %d is already"
1176                                     " in use, destroying link %s",
1177                                     newlink->ll_linkid, newlink->ll_link);
1178                                 link_destroy(newlink);
1179                                 continue;
1180                         }
1181 
1182                         if ((req->ls_flags & DLMGMT_ACTIVE) &&
1183                             link_activate(newlink) != 0) {
1184                                 dlmgmt_log(LOG_WARNING, "Unable to activate %s",
1185                                     newlink->ll_link);
1186                                 link_destroy(newlink);
1187                                 continue;
1188                         }
1189 
1190                         avl_insert(&dlmgmt_id_avl, newlink, id_where);
1191                         /*
1192                          * link_activate call above can insert newlink in
1193                          * dlmgmt_name_avl tree when activating a link that is
1194                          * assigned to a NGZ.
1195                          */
1196                         if (avl_find(&dlmgmt_name_avl, newlink,
1197                             &name_where) == NULL)
1198                                 avl_insert(&dlmgmt_name_avl, newlink,
1199                                     name_where);
1200 
1201                         dlmgmt_advance(newlink);
1202                         newlink->ll_flags |= req->ls_flags;
1203                 }
1204         }
1205 
1206         return (err);
1207 }
1208 
1209 /*
1210  * Generate an entry in the link database.
1211  * Each entry has this format:
1212  * <link name>    <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1213  */
1214 static void
1215 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1216 {
1217         char                    tmpbuf[MAXLINELEN];
1218         char                    *ptr = tmpbuf;
1219         char                    *lim = tmpbuf + MAXLINELEN;
1220         dlmgmt_linkattr_t       *cur_p = NULL;
1221         uint64_t                u64;
1222 
1223         ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1224         if (!persist) {
1225                 /*
1226                  * We store the linkid in the active database so that dlmgmtd
1227                  * can recover in the event that it is restarted.
1228                  */
1229                 u64 = linkp->ll_linkid;
1230                 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1231         }
1232         u64 = linkp->ll_class;
1233         ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1234         u64 = linkp->ll_media;
1235         ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1236 
1237         /*
1238          * The daemon does not keep any active link attribute. Only store the
1239          * attributes if this request is for persistent configuration,
1240          */
1241         if (persist) {
1242                 for (cur_p = linkp->ll_head; cur_p != NULL;
1243                     cur_p = cur_p->lp_next) {
1244                         ptr += translators[cur_p->lp_type].write_func(ptr,
1245                             BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1246                 }
1247         }
1248 
1249         if (ptr <= lim)
1250                 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1251 }
1252 
1253 int
1254 dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1255 {
1256         return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
1257             flags));
1258 }
1259 
1260 int
1261 dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp,
1262     uint32_t flags)
1263 {
1264         int err;
1265 
1266         if (flags & DLMGMT_PERSIST) {
1267                 if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1268                     linkp, DLMGMT_PERSIST)) != 0) {
1269                         return (err);
1270                 }
1271         }
1272 
1273         if (flags & DLMGMT_ACTIVE) {
1274                 if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1275                     linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) {
1276                         (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname,
1277                             linkp, DLMGMT_PERSIST);
1278                         return (err);
1279                 }
1280         }
1281 
1282         return (0);
1283 }
1284 
1285 /*
1286  * Upgrade properties that have link IDs as values to link names.  Because '.'
1287  * is a valid linkname character, the port separater for link aggregations
1288  * must be changed to ':'.
1289  */
1290 static void
1291 linkattr_upgrade(dlmgmt_linkattr_t *attrp)
1292 {
1293         datalink_id_t   linkid;
1294         char            *portidstr;
1295         char            portname[MAXLINKNAMELEN + 1];
1296         dlmgmt_link_t   *linkp;
1297         char            *new_attr_val;
1298         size_t          new_attr_sz;
1299         boolean_t       upgraded = B_FALSE;
1300 
1301         if (strcmp(attrp->lp_name, "linkover") == 0 ||
1302             strcmp(attrp->lp_name, "simnetpeer") == 0) {
1303                 if (attrp->lp_type == DLADM_TYPE_UINT64) {
1304                         linkid = (datalink_id_t)*(uint64_t *)attrp->lp_val;
1305                         if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL)
1306                                 return;
1307                         new_attr_sz = strlen(linkp->ll_link) + 1;
1308                         if ((new_attr_val = malloc(new_attr_sz)) == NULL)
1309                                 return;
1310                         (void) strcpy(new_attr_val, linkp->ll_link);
1311                         upgraded = B_TRUE;
1312                 }
1313         } else if (strcmp(attrp->lp_name, "portnames") == 0) {
1314                 /*
1315                  * The old format for "portnames" was
1316                  * "<linkid>.[<linkid>.]...".  The new format is
1317                  * "<linkname>:[<linkname>:]...".
1318                  */
1319                 if (!isdigit(((char *)attrp->lp_val)[0]))
1320                         return;
1321                 new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char));
1322                 if (new_attr_val == NULL)
1323                         return;
1324                 portidstr = (char *)attrp->lp_val;
1325                 while (*portidstr != '\0') {
1326                         errno = 0;
1327                         linkid = strtol(portidstr, &portidstr, 10);
1328                         if (linkid == 0 || *portidstr != '.' ||
1329                             (linkp = link_by_id(linkid, GLOBAL_ZONEID)) ==
1330                             NULL) {
1331                                 free(new_attr_val);
1332                                 return;
1333                         }
1334                         (void) snprintf(portname, sizeof (portname), "%s:",
1335                             linkp->ll_link);
1336                         if (strlcat(new_attr_val, portname,
1337                             MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) {
1338                                 free(new_attr_val);
1339                                 return;
1340                         }
1341                         /* skip the '.' delimiter */
1342                         portidstr++;
1343                 }
1344                 new_attr_sz = strlen(new_attr_val) + 1;
1345                 upgraded = B_TRUE;
1346         }
1347 
1348         if (upgraded) {
1349                 attrp->lp_type = DLADM_TYPE_STR;
1350                 attrp->lp_sz = new_attr_sz;
1351                 free(attrp->lp_val);
1352                 attrp->lp_val = new_attr_val;
1353         }
1354 }
1355 
1356 static void
1357 dlmgmt_db_upgrade(dlmgmt_link_t *linkp)
1358 {
1359         dlmgmt_linkattr_t *attrp;
1360 
1361         for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next)
1362                 linkattr_upgrade(attrp);
1363 }
1364 
1365 static void
1366 dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1367 {
1368         linkp->ll_flags |= DLMGMT_ACTIVE;
1369         (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1370 }
1371 
1372 static void
1373 dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1374 {
1375         dlmgmt_link_t *linkp;
1376 
1377         for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1378             linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1379                 if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1380                         func(linkp);
1381         }
1382 }
1383 
1384 /*
1385  * Initialize the datalink <link name, linkid> mapping and the link's
1386  * attributes list based on the configuration file /etc/dladm/datalink.conf
1387  * and the active configuration cache file
1388  * /etc/svc/volatile/dladm/datalink-management:default.cache.
1389  */
1390 int
1391 dlmgmt_db_init(zoneid_t zoneid)
1392 {
1393         dlmgmt_db_req_t *req;
1394         int             err;
1395         boolean_t       boot = B_FALSE;
1396 
1397         if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1398             DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1399                 return (err);
1400 
1401         if ((err = dlmgmt_process_db_req(req)) != 0) {
1402                 /*
1403                  * If we get back ENOENT, that means that the active
1404                  * configuration file doesn't exist yet, and is not an error.
1405                  * We'll create it down below after we've loaded the
1406                  * persistent configuration.
1407                  */
1408                 if (err != ENOENT)
1409                         goto done;
1410                 boot = B_TRUE;
1411         }
1412 
1413         req->ls_flags = DLMGMT_PERSIST;
1414         err = dlmgmt_process_db_req(req);
1415         if (err != 0 && err != ENOENT)
1416                 goto done;
1417         err = 0;
1418         if (rewrite_needed) {
1419                 /*
1420                  * First update links in memory, then dump the entire db to
1421                  * disk.
1422                  */
1423                 dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1424                 req->ls_op = DLMGMT_DB_OP_WRITE;
1425                 req->ls_linkid = DATALINK_ALL_LINKID;
1426                 if ((err = dlmgmt_process_db_req(req)) != 0 &&
1427                     err != EINPROGRESS)
1428                         goto done;
1429         }
1430         if (boot) {
1431                 dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1432                     dlmgmt_db_phys_activate);
1433         }
1434 
1435 done:
1436         if (err == EINPROGRESS)
1437                 err = 0;
1438         else
1439                 free(req);
1440         return (err);
1441 }
1442 
1443 /*
1444  * Remove all links in the given zoneid.
1445  */
1446 void
1447 dlmgmt_db_fini(zoneid_t zoneid)
1448 {
1449         dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1450 
1451         while (linkp != NULL) {
1452                 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1453                 if (linkp->ll_zoneid == zoneid) {
1454                         (void) dlmgmt_destroy_common(linkp,
1455                             DLMGMT_ACTIVE | DLMGMT_PERSIST);
1456                 }
1457                 linkp = next_linkp;
1458         }
1459 }