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