xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_db.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 }
1460