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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 * Copyright 2026 Oxide Computer Company
26 */
27
28 #include "libdevinfo.h"
29 #include "devinfo_devlink.h"
30 #include "device_info.h"
31 #include <syslog.h>
32
33 #undef DEBUG
34 #ifndef DEBUG
35 #define NDEBUG 1
36 #else
37 #undef NDEBUG
38 #endif
39
40 #include <assert.h>
41 #include <upanic.h>
42
43 static mutex_t update_mutex = DEFAULTMUTEX; /* Protects update record lock */
44 static mutex_t temp_file_mutex = DEFAULTMUTEX; /* for file creation tests */
45
46 static const size_t elem_sizes[DB_TYPES] = {
47 sizeof (struct db_node),
48 sizeof (struct db_minor),
49 sizeof (struct db_link),
50 sizeof (char)
51 };
52
53 /*
54 * List of directories/files skipped while physically walking /dev
55 * Paths are relative to "<root>/dev/"
56 */
57 static const char *skip_dirs[] = {"fd"};
58 static const char *skip_files[] = {
59 "stdout",
60 "stdin",
61 "stderr"
62 };
63
64 #define N_SKIP_DIRS (sizeof (skip_dirs) / sizeof (skip_dirs[0]))
65 #define N_SKIP_FILES (sizeof (skip_files) / sizeof (skip_files[0]))
66
67 #define DI_TEST_DB ETCDEV "di_test_db"
68
69 /*
70 *
71 * This file contains two sets of interfaces which operate on the reverse
72 * links database. One set (which includes di_devlink_open()/_close())
73 * allows link generators like devfsadm(8) and ucblinks(1B) (writers) to
74 * populate the database with /devices -> /dev mappings. Another set
75 * of interfaces (which includes di_devlink_init()/_fini()) allows
76 * applications (readers) to lookup the database for /dev links corresponding
77 * to a given minor.
78 *
79 * Writers operate on a cached version of the database. The cache is created
80 * when di_devlink_open() is called. As links in /dev are created and removed,
81 * the cache is updated to keep it in synch with /dev. When the /dev updates
82 * are complete, the link generator calls di_devlink_close() which writes
83 * out the cache to the database.
84 *
85 * Applications which need to lookup the database, call di_devlink_init().
86 * di_devlink_init() checks the database file (if one exists). If the
87 * database is valid, it is mapped into the address space of the
88 * application. The database file consists of several segments. Each
89 * segment can be mapped in independently and is mapped on demand.
90 *
91 * Database Layout
92 *
93 * ---------------------
94 * | Magic # |
95 * | ----------------- |
96 * | Version | HEADER
97 * | ----------------- |
98 * | ... |
99 * ---------------------
100 * | |
101 * | | NODES
102 * | |
103 * | |
104 * ---------------------
105 * | |
106 * | | MINORS
107 * | |
108 * | |
109 * ---------------------
110 * | |
111 * | | LINKS
112 * | |
113 * | |
114 * ---------------------
115 * | |
116 * | | STRINGS
117 * | |
118 * | |
119 * ---------------------
120 *
121 * Readers can lookup /dev links for a specific minor or
122 * lookup all /dev links. In the latter case, the node
123 * and minor segments are not mapped in and the reader
124 * walks through every link in the link segment.
125 *
126 */
127 di_devlink_handle_t
di_devlink_open(const char * root_dir,uint_t flags)128 di_devlink_open(const char *root_dir, uint_t flags)
129 {
130 int err;
131 char path[PATH_MAX];
132 struct di_devlink_handle *hdp;
133 int retried = 0;
134
135 retry:
136 /*
137 * Allocate a read-write handle but open the DB in readonly
138 * mode. We do writes only to a temporary copy of the database.
139 */
140 if ((hdp = handle_alloc(root_dir, OPEN_RDWR)) == NULL) {
141 return (NULL);
142 }
143
144 err = open_db(hdp, OPEN_RDONLY);
145
146 /*
147 * We don't want to unlink the db at this point - if we did we
148 * would be creating a window where consumers would take a slow
149 * code path (and those consumers might also trigger requests for
150 * db creation, which we are already in the process of doing).
151 * When we are done with our update, we use rename to install the
152 * latest version of the db file.
153 */
154 get_db_path(hdp, DB_FILE, path, sizeof (path));
155
156 /*
157 * The flags argument is reserved for future use.
158 */
159 if (flags != 0) {
160 handle_free(&hdp); /* also closes the DB */
161 errno = EINVAL;
162 return (NULL);
163 }
164
165 if (cache_alloc(hdp) != 0) {
166 handle_free(&hdp);
167 return (NULL);
168 }
169
170 if (err) {
171 /*
172 * Failed to open DB.
173 * The most likely cause is that DB file did not exist.
174 * Call di_devlink_close() to recreate the DB file and
175 * retry di_devlink_open().
176 */
177 if (retried == 0) {
178 (void) di_devlink_close(&hdp, 0);
179 retried = 1;
180 goto retry;
181 }
182
183 /*
184 * DB cannot be opened, just return the
185 * handle. We will recreate the DB later.
186 */
187 return (hdp);
188 }
189
190 /* Read the database into the cache */
191 CACHE(hdp)->update_count = DB_HDR(hdp)->update_count;
192 (void) read_nodes(hdp, NULL, DB_HDR(hdp)->root_idx);
193 (void) read_links(hdp, NULL, DB_HDR(hdp)->dngl_idx);
194
195 (void) close_db(hdp);
196
197 return (hdp);
198 }
199
200 static void
get_db_path(struct di_devlink_handle * hdp,const char * fname,char * buf,size_t blen)201 get_db_path(
202 struct di_devlink_handle *hdp,
203 const char *fname,
204 char *buf,
205 size_t blen)
206 {
207 char *dir = NULL;
208
209 #ifdef DEBUG
210 if ((dir = getenv(ALT_DB_DIR)) != NULL) {
211 (void) devlink_dprintf(DBG_INFO,
212 "get_db_path: alternate db dir: %s\n", dir);
213 }
214 #endif
215 if (dir == NULL) {
216 dir = hdp->db_dir;
217 }
218
219 (void) snprintf(buf, blen, "%s/%s", dir, fname);
220 }
221
222 static int
open_db(struct di_devlink_handle * hdp,int flags)223 open_db(struct di_devlink_handle *hdp, int flags)
224 {
225 size_t sz;
226 long page_sz;
227 int fd, rv, flg;
228 struct stat sbuf;
229 uint32_t count[DB_TYPES] = {0};
230 char path[PATH_MAX];
231 void *cp;
232
233 assert(!DB_OPEN(hdp));
234
235 #ifdef DEBUG
236 if (getenv(SKIP_DB)) {
237 (void) devlink_dprintf(DBG_INFO,
238 "open_db: skipping database\n");
239 return (-1);
240 }
241 #endif
242 if ((page_sz = sysconf(_SC_PAGE_SIZE)) == -1) {
243 return (-1);
244 }
245
246 /*
247 * Use O_TRUNC flag for write access, so that the subsequent ftruncate()
248 * call will zero-fill the entire file
249 */
250 if (IS_RDONLY(flags)) {
251 flg = O_RDONLY;
252 get_db_path(hdp, DB_FILE, path, sizeof (path));
253 } else {
254 flg = O_RDWR|O_CREAT|O_TRUNC;
255 get_db_path(hdp, DB_TMP, path, sizeof (path));
256 }
257
258 /*
259 * Avoid triggering /dev reconfigure for read when not present
260 */
261 if (IS_RDONLY(flags) &&
262 (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) {
263 return (-1);
264 }
265
266 if ((fd = open(path, flg, DB_PERMS)) == -1) {
267 return (-1);
268 }
269
270 if (IS_RDONLY(flags)) {
271 flg = PROT_READ;
272 rv = fstat(fd, &sbuf);
273 sz = sbuf.st_size;
274 } else {
275 flg = PROT_READ | PROT_WRITE;
276 sz = size_db(hdp, page_sz, count);
277 rv = ftruncate(fd, sz);
278 }
279
280 if (rv == -1 || sz < HDR_LEN) {
281 if (rv != -1)
282 errno = EINVAL;
283 (void) close(fd);
284 return (-1);
285 }
286
287 cp = mmap(0, HDR_LEN, flg, MAP_SHARED, fd, 0);
288 if (cp == MAP_FAILED) {
289 (void) close(fd);
290 return (-1);
291 }
292 DB(hdp)->hdr = (struct db_hdr *)cp;
293 DB(hdp)->db_fd = fd;
294 DB(hdp)->flags = flags;
295
296 if (IS_RDONLY(flags)) {
297 rv = invalid_db(hdp, sz, page_sz);
298 } else {
299 rv = init_hdr(hdp, page_sz, count);
300 /*
301 * Start the running string CRC with the NIL string which
302 * occupies the first byte of the string segment.
303 */
304 DB(hdp)->str_crc = devlink_crc32(0, "", 1);
305 }
306
307 if (rv) {
308 (void) devlink_dprintf(DBG_ERR, "open_db: invalid DB(%s)\n",
309 path);
310 (void) close_db(hdp);
311 return (-1);
312 }
313
314 /*
315 * Verify database integrity before it is used.
316 */
317 if (IS_RDONLY(flags) && !verify_db_crc(hdp)) {
318 (void) devlink_dprintf(DBG_ERR,
319 "open_db: DB(%s): CRC mismatch\n", path);
320 (void) close_db(hdp);
321 return (-1);
322 }
323
324 (void) devlink_dprintf(DBG_STEP, "open_db: DB(%s): opened\n", path);
325 return (0);
326 }
327
328 /*
329 * A handle can be allocated for read-only or read-write access
330 */
331 static struct di_devlink_handle *
handle_alloc(const char * root_dir,uint_t flags)332 handle_alloc(const char *root_dir, uint_t flags)
333 {
334 char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX];
335 struct di_devlink_handle *hdp, proto = {0};
336 int install = 0;
337 int isroot = 0;
338 struct stat sb;
339 char can_path[PATH_MAX];
340
341 assert(flags == OPEN_RDWR || flags == OPEN_RDONLY);
342
343 dev_dir[0] = '\0';
344 db_dir[0] = '\0';
345
346 /*
347 * NULL and the empty string are equivalent to "/"
348 */
349 if (root_dir && root_dir[0] != '\0') {
350
351 if (root_dir[0] != '/') {
352 errno = EINVAL;
353 return (NULL);
354 }
355
356 #ifdef DEBUG
357 /*LINTED*/
358 assert(sizeof (dev_dir) >= PATH_MAX);
359 #endif
360 if ((realpath(root_dir, dev_dir) == NULL) ||
361 (realpath(root_dir, db_dir) == NULL)) {
362 return (NULL);
363 }
364 } else {
365 /*
366 * The dev dir is at /dev i.e. we are not doing a -r /altroot
367 */
368 isroot = 1;
369 }
370
371 if (strcmp(dev_dir, "/") == 0) {
372 dev_dir[0] = 0;
373 db_dir[0] = 0;
374 } else {
375 (void) strlcpy(db_dir, dev_dir, sizeof (db_dir));
376 }
377
378 (void) strlcat(dev_dir, DEV, sizeof (dev_dir));
379 (void) strlcat(db_dir, ETCDEV, sizeof (db_dir));
380
381 /*
382 * The following code is for install. Readers and writers need
383 * to be redirected to /tmp/etc/dev for the database file.
384 * Note that we test for readonly /etc by actually creating a
385 * file since statvfs is not a reliable method for determining
386 * readonly filesystems.
387 */
388 install = 0;
389 (void) snprintf(can_path, sizeof (can_path), "%s/%s", ETCDEV, DB_FILE);
390 if (flags == OPEN_RDWR && isroot) {
391 char di_test_db[PATH_MAX];
392 int fd;
393 (void) mutex_lock(&temp_file_mutex);
394 (void) snprintf(di_test_db, sizeof (di_test_db), "%s.%d",
395 DI_TEST_DB, getpid());
396 fd = open(di_test_db, O_CREAT|O_RDWR|O_EXCL, 0644);
397 if (fd == -1 && errno == EROFS && stat(can_path, &sb) == -1)
398 install = 1;
399 if (fd != -1) {
400 (void) close(fd);
401 (void) unlink(di_test_db);
402 }
403 (void) mutex_unlock(&temp_file_mutex);
404 } else if (isroot) {
405 /*
406 * Readers can be non-privileged so we cannot test by creating
407 * a file in /etc/dev. Instead we check if the database
408 * file is missing in /etc/dev and is present in /tmp/etc/dev
409 * and is owned by root.
410 */
411 char install_path[PATH_MAX];
412
413 (void) snprintf(install_path, sizeof (install_path),
414 "/tmp%s/%s", ETCDEV, DB_FILE);
415 if (stat(can_path, &sb) == -1 && stat(install_path, &sb)
416 != -1 && sb.st_uid == 0) {
417 install = 1;
418 }
419 }
420
421 /*
422 * Check if we are in install. If we are, the database will be in
423 * /tmp/etc/dev
424 */
425 if (install)
426 (void) snprintf(db_dir, sizeof (db_dir), "/tmp%s", ETCDEV);
427
428 proto.dev_dir = dev_dir;
429 proto.db_dir = db_dir;
430 proto.flags = flags;
431 proto.lock_fd = -1;
432
433 /*
434 * Lock database if a read-write handle is being allocated.
435 * Locks are needed to protect against multiple writers.
436 * Readers don't need locks.
437 */
438 if (HDL_RDWR(&proto)) {
439 if (enter_db_lock(&proto, root_dir) != 1) {
440 return (NULL);
441 }
442 }
443
444 DB(&proto)->db_fd = -1;
445
446 hdp = calloc(1, sizeof (struct di_devlink_handle));
447 if (hdp == NULL) {
448 goto error;
449 }
450
451 *hdp = proto;
452
453 /*
454 * The handle hdp now contains a pointer to local storage
455 * in the dev_dir field (obtained from the proto handle).
456 * In the following line, a dynamically allocated version
457 * is substituted.
458 */
459
460 if ((hdp->dev_dir = strdup(proto.dev_dir)) == NULL) {
461 free(hdp);
462 goto error;
463 }
464
465 if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) {
466 free(hdp->dev_dir);
467 free(hdp);
468 goto error;
469 }
470
471 return (hdp);
472
473 error:
474 if (HDL_RDWR(&proto)) {
475 /* Unlink DB file on error */
476 get_db_path(&proto, DB_FILE, path, sizeof (path));
477 (void) unlink(path);
478 exit_db_lock(&proto);
479 }
480 return (NULL);
481 }
482
483
484 static int
cache_alloc(struct di_devlink_handle * hdp)485 cache_alloc(struct di_devlink_handle *hdp)
486 {
487 size_t hash_sz = 0;
488
489 assert(HDL_RDWR(hdp));
490
491 if (DB_OPEN(hdp)) {
492 hash_sz = DB_NUM(hdp, DB_LINK) / AVG_CHAIN_SIZE;
493 }
494 hash_sz = (hash_sz >= MIN_HASH_SIZE) ? hash_sz : MIN_HASH_SIZE;
495
496 CACHE(hdp)->hash = calloc(hash_sz, sizeof (cache_link_t *));
497 if (CACHE(hdp)->hash == NULL) {
498 return (-1);
499 }
500 CACHE(hdp)->hash_sz = hash_sz;
501
502 return (0);
503 }
504
505
506 static int
invalid_db(struct di_devlink_handle * hdp,size_t fsize,long page_sz)507 invalid_db(struct di_devlink_handle *hdp, size_t fsize, long page_sz)
508 {
509 int i;
510 char *cp;
511 size_t sz;
512
513 if (DB_HDR(hdp)->magic != DB_MAGIC || DB_HDR(hdp)->vers != DB_VERSION) {
514 return (1);
515 }
516
517 if (DB_HDR(hdp)->page_sz == 0 || DB_HDR(hdp)->page_sz != page_sz) {
518 return (1);
519 }
520
521 sz = seg_size(hdp, DB_HEADER);
522 for (i = 0; i < DB_TYPES; i++) {
523 (void) devlink_dprintf(DBG_INFO, "N[%u] = %u\n", i,
524 DB_NUM(hdp, i));
525 /* There must be at least 1 element of each type */
526 if (DB_NUM(hdp, i) < 1) {
527 return (1);
528 }
529 sz += seg_size(hdp, i);
530 assert(sz % page_sz == 0);
531 }
532
533 if (sz != fsize) {
534 return (1);
535 }
536
537 if (!VALID_INDEX(hdp, DB_NODE, DB_HDR(hdp)->root_idx)) {
538 return (1);
539 }
540
541 if (!VALID_INDEX(hdp, DB_LINK, DB_HDR(hdp)->dngl_idx)) {
542 return (1);
543 }
544
545 if (DB_EMPTY(hdp)) {
546 return (1);
547 }
548
549 /*
550 * The last character in the string segment must be a NUL char.
551 */
552 cp = get_string(hdp, DB_NUM(hdp, DB_STR) - 1);
553 if (cp == NULL || *cp != '\0') {
554 return (1);
555 }
556
557 return (0);
558 }
559
560 static const uint32_t devlink_crc32_table[256] = { CRC32_TABLE };
561
562 static uint32_t
devlink_crc32(uint32_t crc,const void * buf,size_t len)563 devlink_crc32(uint32_t crc, const void *buf, size_t len)
564 {
565 CRC32(crc, buf, len, crc, devlink_crc32_table);
566 return (crc);
567 }
568
569 /*
570 * A corrupt database has been detected. Report it, preserve the evidence by
571 * renaming the database aside, and panic to produce a core file. Renaming
572 * the database also allows the system to recover, since with no database
573 * present the next devfsadm invocation rebuilds it from the kernel and
574 * /dev.
575 */
576 static void
devlink_db_fault(struct di_devlink_handle * hdp,const char * fmt,...)577 devlink_db_fault(struct di_devlink_handle *hdp, const char *fmt, ...)
578 {
579 char msg[1024], from[PATH_MAX], to[PATH_MAX];
580 size_t len;
581 va_list ap;
582 int ret;
583
584 len = strlcpy(msg, "devlink DB corruption: ", sizeof (msg));
585
586 va_start(ap, fmt);
587 ret = vsnprintf(msg + len, sizeof (msg) - len, fmt, ap);
588 va_end(ap);
589 if (ret < 0)
590 msg[len] = '\0';
591
592 syslog(LOG_ALERT, "%s", msg);
593
594 if (hdp != NULL && DB_OPEN(hdp)) {
595 get_db_path(hdp, DB_RDWR(hdp) ? DB_TMP : DB_FILE,
596 from, sizeof (from));
597 get_db_path(hdp, DB_CORRUPT, to, sizeof (to));
598 len = strlen(to);
599 (void) snprintf(to + len, sizeof (to) - len, ".%ld",
600 (long)time(NULL));
601 if (rename(from, to) == 0) {
602 syslog(LOG_ALERT, "quarantined %s as %s", from, to);
603 } else {
604 syslog(LOG_ALERT, "failed to quarantine %s as %s: %s",
605 from, to, strerror(errno));
606 }
607 }
608
609 upanic(msg, strlen(msg) + 1);
610 }
611
612 static uint32_t
segment_crc(struct di_devlink_handle * hdp,db_seg_t seg,int prot)613 segment_crc(struct di_devlink_handle *hdp, db_seg_t seg, int prot)
614 {
615 size_t len;
616
617 if (map_seg(hdp, 1, prot, seg) == NULL)
618 return (0);
619
620 len = DB_NUM(hdp, seg) * elem_sizes[seg];
621 return (devlink_crc32(0, DB_SEG(hdp, seg), len));
622 }
623
624 static bool
verify_db_crc(struct di_devlink_handle * hdp)625 verify_db_crc(struct di_devlink_handle *hdp)
626 {
627 uint32_t crc;
628 int i;
629
630 for (i = 0; i < DB_TYPES; i++) {
631 crc = segment_crc(hdp, i, PROT_READ);
632 if (crc == DB_HDR(hdp)->crc[i])
633 continue;
634
635 syslog(LOG_WARNING, "devlink DB segment %d CRC mismatch "
636 "(stored %08x, computed %08x)",
637 i, DB_HDR(hdp)->crc[i], crc);
638
639 if (HDL_RDWR(hdp)) {
640 devlink_db_fault(hdp, "segment %d CRC mismatch "
641 "(stored %08x, computed %08x); written by "
642 "pid %u (%s) at %llu", i, DB_HDR(hdp)->crc[i],
643 crc, DB_HDR(hdp)->writer_pid,
644 DB_HDR(hdp)->writer_exec,
645 (u_longlong_t)DB_HDR(hdp)->writer_time);
646 }
647 return (false);
648 }
649 return (true);
650 }
651
652 /*
653 * The database content is complete. Record the segment CRCs and the
654 * identity of this writer in the header, after verifying that the string
655 * segment still matches the CRC accumulated from the source strings as
656 * they were copied in. If there's a mismatch then something modified the
657 * mapping after the data was written. Finally, make the data segments
658 * read-only so that any stray store into the mapping before it is
659 * unmapped faults instead of corrupting the file.
660 */
661 static void
seal_db(struct di_devlink_handle * hdp,uint32_t * next)662 seal_db(struct di_devlink_handle *hdp, uint32_t *next)
663 {
664 struct db_hdr *hp = DB_HDR(hdp);
665 const char *exec, *base;
666 uint32_t crc;
667 int i;
668
669 assert(HDL_RDWR(hdp) && DB_RDWR(hdp));
670
671 if (map_seg(hdp, 1, PROT_READ | PROT_WRITE, DB_STR) == NULL) {
672 SET_DB_ERR(hdp);
673 return;
674 }
675
676 crc = devlink_crc32(0, DB_SEG(hdp, DB_STR), next[DB_STR]);
677 if (crc != DB(hdp)->str_crc) {
678 devlink_db_fault(hdp, "string segment modified during "
679 "write (computed %08x, expected %08x)",
680 crc, DB(hdp)->str_crc);
681 }
682
683 for (i = 0; i < DB_TYPES; i++)
684 hp->crc[i] = segment_crc(hdp, i, PROT_READ | PROT_WRITE);
685
686 hp->writer_pid = (uint32_t)getpid();
687 hp->writer_time = (uint64_t)time(NULL);
688 if ((exec = getexecname()) != NULL) {
689 base = strrchr(exec, '/');
690 (void) strlcpy(hp->writer_exec,
691 base != NULL ? base + 1 : exec, sizeof (hp->writer_exec));
692 }
693
694 for (i = 0; i < DB_TYPES; i++) {
695 if (DB_SEG(hdp, i) != NULL && mprotect(DB_SEG(hdp, i),
696 seg_size(hdp, i), PROT_READ) == 0) {
697 DB_SEG_PROT(hdp, i) = PROT_READ;
698 }
699 }
700 }
701
702 static int
read_nodes(struct di_devlink_handle * hdp,cache_node_t * pcnp,uint32_t nidx)703 read_nodes(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
704 {
705 char *path;
706 cache_node_t *cnp;
707 struct db_node *dnp;
708 const char *fcn = "read_nodes";
709
710 assert(HDL_RDWR(hdp));
711
712 /*
713 * parent node should be NULL only for the root node
714 */
715 if ((pcnp == NULL) ^ (nidx == DB_HDR(hdp)->root_idx)) {
716 (void) devlink_dprintf(DBG_ERR,
717 "%s: invalid parent or index(%u)\n", fcn, nidx);
718 SET_DB_ERR(hdp);
719 return (-1);
720 }
721
722 for (; (dnp = get_node(hdp, nidx)) != NULL; nidx = dnp->sib) {
723
724 path = get_string(hdp, dnp->path);
725
726 /*
727 * Insert at head of list to recreate original order
728 */
729 cnp = node_insert(hdp, pcnp, path, INSERT_HEAD);
730 if (cnp == NULL) {
731 SET_DB_ERR(hdp);
732 break;
733 }
734
735 assert(strcmp(path, "/") ^ (nidx == DB_HDR(hdp)->root_idx));
736 assert(strcmp(path, "/") != 0 || dnp->sib == DB_NIL);
737
738 if (read_minors(hdp, cnp, dnp->minor) != 0 ||
739 read_nodes(hdp, cnp, dnp->child) != 0) {
740 break;
741 }
742
743 (void) devlink_dprintf(DBG_STEP, "%s: node[%u]: %s\n",
744 fcn, nidx, cnp->path);
745 }
746
747 return (dnp ? -1 : 0);
748 }
749
750 static int
read_minors(struct di_devlink_handle * hdp,cache_node_t * pcnp,uint32_t nidx)751 read_minors(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
752 {
753 cache_minor_t *cmnp;
754 struct db_minor *dmp;
755 char *name, *nodetype;
756 const char *fcn = "read_minors";
757
758 assert(HDL_RDWR(hdp));
759
760 if (pcnp == NULL) {
761 (void) devlink_dprintf(DBG_ERR, "%s: minor[%u]: orphan minor\n",
762 fcn, nidx);
763 SET_DB_ERR(hdp);
764 return (-1);
765 }
766
767 for (; (dmp = get_minor(hdp, nidx)) != NULL; nidx = dmp->sib) {
768
769 name = get_string(hdp, dmp->name);
770 nodetype = get_string(hdp, dmp->nodetype);
771
772 cmnp = minor_insert(hdp, pcnp, name, nodetype, NULL);
773 if (cmnp == NULL) {
774 SET_DB_ERR(hdp);
775 break;
776 }
777
778 (void) devlink_dprintf(DBG_STEP, "%s: minor[%u]: %s\n",
779 fcn, nidx, cmnp->name);
780
781 if (read_links(hdp, cmnp, dmp->link) != 0) {
782 break;
783 }
784 }
785
786 return (dmp ? -1 : 0);
787 }
788
789 /*
790 * If the link is dangling the corresponding minor will be absent.
791 */
792 static int
read_links(struct di_devlink_handle * hdp,cache_minor_t * pcmp,uint32_t nidx)793 read_links(struct di_devlink_handle *hdp, cache_minor_t *pcmp, uint32_t nidx)
794 {
795 cache_link_t *clp;
796 struct db_link *dlp;
797 char *path, *content;
798
799 assert(HDL_RDWR(hdp));
800
801 if (nidx != DB_NIL &&
802 ((pcmp == NULL) ^ (nidx == DB_HDR(hdp)->dngl_idx))) {
803 (void) devlink_dprintf(DBG_ERR, "read_links: invalid minor or"
804 " index(%u)\n", nidx);
805 SET_DB_ERR(hdp);
806 return (-1);
807 }
808
809 for (; (dlp = get_link(hdp, nidx)) != NULL; nidx = dlp->sib) {
810
811 path = get_string(hdp, dlp->path);
812 content = get_string(hdp, dlp->content);
813
814 if (link_hash(hdp, path, 0) != NULL) {
815 (void) devlink_dprintf(DBG_ERR,
816 "read_links: skipping duplicate link[%u]: "
817 "%s\n", nidx, path ? path : "<NULL>");
818 continue;
819 }
820
821 clp = link_insert(hdp, pcmp, path, content, dlp->attr);
822 if (clp == NULL) {
823 SET_DB_ERR(hdp);
824 break;
825 }
826
827 (void) devlink_dprintf(DBG_STEP, "read_links: link[%u]: %s%s\n",
828 nidx, clp->path, pcmp == NULL ? "(DANGLING)" : "");
829 }
830
831 return (dlp ? -1 : 0);
832 }
833
834 int
di_devlink_close(di_devlink_handle_t * pp,int flag)835 di_devlink_close(di_devlink_handle_t *pp, int flag)
836 {
837 int i, rv;
838 char tmp[PATH_MAX];
839 char file[PATH_MAX];
840 uint32_t next[DB_TYPES] = {0};
841 struct di_devlink_handle *hdp;
842
843 if (pp == NULL || *pp == NULL || !HDL_RDWR(*pp)) {
844 errno = EINVAL;
845 return (-1);
846 }
847
848 hdp = *pp;
849 *pp = NULL;
850
851 /*
852 * The caller encountered some error in their processing.
853 * so handle isn't valid. Discard it and return success.
854 */
855 if (flag == DI_LINK_ERROR) {
856 handle_free(&hdp);
857 return (0);
858 }
859
860 if (DB_ERR(hdp)) {
861 handle_free(&hdp);
862 errno = EINVAL;
863 return (-1);
864 }
865
866 /*
867 * Extract the DB path before the handle is freed.
868 */
869 get_db_path(hdp, DB_FILE, file, sizeof (file));
870 get_db_path(hdp, DB_TMP, tmp, sizeof (tmp));
871
872 /*
873 * update database with actual contents of /dev
874 */
875 (void) devlink_dprintf(DBG_INFO,
876 "di_devlink_close: update_count = %u\n", CACHE(hdp)->update_count);
877
878 /*
879 * For performance reasons, synchronization of the database
880 * with /dev is turned off by default. However, applications
881 * with appropriate permissions can request a "sync" by
882 * calling di_devlink_update().
883 */
884 if (CACHE(hdp)->update_count == 0) {
885 CACHE(hdp)->update_count = 1;
886 (void) devlink_dprintf(DBG_INFO,
887 "di_devlink_close: synchronizing DB\n");
888 (void) synchronize_db(hdp);
889 }
890
891 /*
892 * Resolve dangling links AFTER synchronizing DB with /dev as the
893 * synchronization process may create dangling links.
894 */
895 resolve_dangling_links(hdp);
896
897 /*
898 * All changes to the cache are complete. Write out the cache
899 * to the database only if it is not empty.
900 */
901 if (CACHE_EMPTY(hdp)) {
902 (void) devlink_dprintf(DBG_INFO,
903 "di_devlink_close: skipping write\n");
904 (void) unlink(file);
905 handle_free(&hdp);
906 return (0);
907 }
908
909 if (open_db(hdp, OPEN_RDWR) != 0) {
910 handle_free(&hdp);
911 return (-1);
912 }
913
914 /*
915 * Keep track of array assignments. There is at least
916 * 1 element (the "NIL" element) per type.
917 */
918 for (i = 0; i < DB_TYPES; i++) {
919 next[i] = 1;
920 }
921
922 (void) write_nodes(hdp, NULL, CACHE_ROOT(hdp), next);
923 (void) write_links(hdp, NULL, CACHE(hdp)->dngl, next);
924 DB_HDR(hdp)->update_count = CACHE(hdp)->update_count;
925
926 if (!DB_ERR(hdp))
927 seal_db(hdp, next);
928
929 rv = close_db(hdp);
930
931 if (rv != 0 || DB_ERR(hdp) || rename(tmp, file) != 0) {
932 (void) devlink_dprintf(DBG_ERR,
933 "di_devlink_close: %s error: %s\n",
934 rv ? "close_db" : "DB or rename", strerror(errno));
935 (void) unlink(tmp);
936 (void) unlink(file);
937 handle_free(&hdp);
938 return (-1);
939 }
940
941 handle_free(&hdp);
942
943 (void) devlink_dprintf(DBG_INFO, "di_devlink_close: wrote DB(%s)\n",
944 file);
945
946 return (0);
947 }
948
949 /*
950 * Inits the database header.
951 */
952 static int
init_hdr(struct di_devlink_handle * hdp,long page_sz,uint32_t * count)953 init_hdr(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
954 {
955 int i;
956
957 DB_HDR(hdp)->magic = DB_MAGIC;
958 DB_HDR(hdp)->vers = DB_VERSION;
959 DB_HDR(hdp)->root_idx = DB_NIL;
960 DB_HDR(hdp)->dngl_idx = DB_NIL;
961 DB_HDR(hdp)->page_sz = (uint32_t)page_sz;
962
963 for (i = 0; i < DB_TYPES; i++) {
964 assert(count[i] >= 1);
965 DB_NUM(hdp, i) = count[i];
966 }
967
968 return (0);
969 }
970
971 static int
write_nodes(struct di_devlink_handle * hdp,struct db_node * pdnp,cache_node_t * cnp,uint32_t * next)972 write_nodes(
973 struct di_devlink_handle *hdp,
974 struct db_node *pdnp,
975 cache_node_t *cnp,
976 uint32_t *next)
977 {
978 uint32_t idx;
979 struct db_node *dnp;
980 const char *fcn = "write_nodes";
981
982 assert(HDL_RDWR(hdp));
983
984 for (; cnp != NULL; cnp = cnp->sib) {
985
986 assert(cnp->path != NULL);
987
988 /* parent node should only be NULL for root node */
989 if ((pdnp == NULL) ^ (cnp == CACHE_ROOT(hdp))) {
990 (void) devlink_dprintf(DBG_ERR,
991 "%s: invalid parent for: %s\n", fcn, cnp->path);
992 SET_DB_ERR(hdp);
993 break;
994 }
995
996 assert((strcmp(cnp->path, "/") != 0) ^
997 (cnp == CACHE_ROOT(hdp)));
998
999 idx = next[DB_NODE];
1000 if ((dnp = set_node(hdp, idx)) == NULL) {
1001 SET_DB_ERR(hdp);
1002 break;
1003 }
1004
1005 dnp->path = write_string(hdp, cnp->path, next);
1006 if (dnp->path == DB_NIL) {
1007 SET_DB_ERR(hdp);
1008 break;
1009 }
1010 /* commit write for this node */
1011 next[DB_NODE]++;
1012
1013 if (pdnp == NULL) {
1014 assert(DB_HDR(hdp)->root_idx == DB_NIL);
1015 DB_HDR(hdp)->root_idx = idx;
1016 } else {
1017 dnp->sib = pdnp->child;
1018 pdnp->child = idx;
1019 }
1020
1021 (void) devlink_dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, idx,
1022 cnp->path);
1023
1024 if (write_minors(hdp, dnp, cnp->minor, next) != 0 ||
1025 write_nodes(hdp, dnp, cnp->child, next) != 0) {
1026 break;
1027 }
1028 }
1029
1030 return (cnp ? -1 : 0);
1031 }
1032
1033 static int
write_minors(struct di_devlink_handle * hdp,struct db_node * pdnp,cache_minor_t * cmnp,uint32_t * next)1034 write_minors(
1035 struct di_devlink_handle *hdp,
1036 struct db_node *pdnp,
1037 cache_minor_t *cmnp,
1038 uint32_t *next)
1039 {
1040 uint32_t idx;
1041 struct db_minor *dmp;
1042 const char *fcn = "write_minors";
1043
1044 assert(HDL_RDWR(hdp));
1045
1046 if (pdnp == NULL) {
1047 (void) devlink_dprintf(DBG_ERR, "%s: no node for minor: %s\n",
1048 fcn, cmnp ? cmnp->name : "<NULL>");
1049 SET_DB_ERR(hdp);
1050 return (-1);
1051 }
1052
1053 for (; cmnp != NULL; cmnp = cmnp->sib) {
1054
1055 assert(cmnp->name != NULL);
1056
1057 idx = next[DB_MINOR];
1058 if ((dmp = set_minor(hdp, idx)) == NULL) {
1059 SET_DB_ERR(hdp);
1060 break;
1061 }
1062
1063 dmp->name = write_string(hdp, cmnp->name, next);
1064 dmp->nodetype = write_string(hdp, cmnp->nodetype, next);
1065 if (dmp->name == DB_NIL || dmp->nodetype == DB_NIL) {
1066 dmp->name = dmp->nodetype = DB_NIL;
1067 SET_DB_ERR(hdp);
1068 break;
1069 }
1070
1071 /* Commit writes to this minor */
1072 next[DB_MINOR]++;
1073
1074 dmp->sib = pdnp->minor;
1075 pdnp->minor = idx;
1076
1077 (void) devlink_dprintf(DBG_STEP, "%s: minor[%u]: %s\n",
1078 fcn, idx, cmnp->name);
1079
1080 if (write_links(hdp, dmp, cmnp->link, next) != 0) {
1081 break;
1082 }
1083 }
1084
1085 return (cmnp ? -1 : 0);
1086 }
1087
1088 static int
write_links(struct di_devlink_handle * hdp,struct db_minor * pdmp,cache_link_t * clp,uint32_t * next)1089 write_links(
1090 struct di_devlink_handle *hdp,
1091 struct db_minor *pdmp,
1092 cache_link_t *clp,
1093 uint32_t *next)
1094 {
1095 uint32_t idx;
1096 struct db_link *dlp;
1097 const char *fcn = "write_links";
1098
1099 assert(HDL_RDWR(hdp));
1100
1101 /* A NULL minor if and only if the links are dangling */
1102 if (clp != NULL && ((pdmp == NULL) ^ (clp == CACHE(hdp)->dngl))) {
1103 (void) devlink_dprintf(DBG_ERR, "%s: invalid minor for link\n",
1104 fcn);
1105 SET_DB_ERR(hdp);
1106 return (-1);
1107 }
1108
1109 for (; clp != NULL; clp = clp->sib) {
1110
1111 assert(clp->path != NULL);
1112
1113 if ((pdmp == NULL) ^ (clp->minor == NULL)) {
1114 (void) devlink_dprintf(DBG_ERR,
1115 "%s: invalid minor for link(%s)\n", fcn, clp->path);
1116 SET_DB_ERR(hdp);
1117 break;
1118 }
1119
1120 idx = next[DB_LINK];
1121 if ((dlp = set_link(hdp, idx)) == NULL) {
1122 SET_DB_ERR(hdp);
1123 break;
1124 }
1125
1126 dlp->path = write_string(hdp, clp->path, next);
1127 dlp->content = write_string(hdp, clp->content, next);
1128 if (dlp->path == DB_NIL || dlp->content == DB_NIL) {
1129 dlp->path = dlp->content = DB_NIL;
1130 SET_DB_ERR(hdp);
1131 break;
1132 }
1133
1134 dlp->attr = clp->attr;
1135
1136 /* Commit writes to this link */
1137 next[DB_LINK]++;
1138
1139 if (pdmp != NULL) {
1140 dlp->sib = pdmp->link;
1141 pdmp->link = idx;
1142 } else {
1143 dlp->sib = DB_HDR(hdp)->dngl_idx;
1144 DB_HDR(hdp)->dngl_idx = idx;
1145 }
1146
1147 (void) devlink_dprintf(DBG_STEP, "%s: link[%u]: %s%s\n",
1148 fcn, idx, clp->path, pdmp == NULL ? "(DANGLING)" : "");
1149 }
1150
1151 return (clp ? -1 : 0);
1152 }
1153
1154
1155 static uint32_t
write_string(struct di_devlink_handle * hdp,const char * str,uint32_t * next)1156 write_string(struct di_devlink_handle *hdp, const char *str, uint32_t *next)
1157 {
1158 char *dstr;
1159 uint32_t idx;
1160
1161 assert(HDL_RDWR(hdp));
1162
1163 if (str == NULL) {
1164 (void) devlink_dprintf(DBG_ERR,
1165 "write_string: NULL argument\n");
1166 return (DB_NIL);
1167 }
1168
1169 idx = next[DB_STR];
1170 if (!VALID_STR(hdp, idx, str)) {
1171 (void) devlink_dprintf(DBG_ERR,
1172 "write_string: invalid index[%u], string(%s)\n", idx, str);
1173 return (DB_NIL);
1174 }
1175
1176 if ((dstr = set_string(hdp, idx)) == NULL) {
1177 return (DB_NIL);
1178 }
1179
1180 (void) strcpy(dstr, str);
1181
1182 /*
1183 * Verify that the string just written to the database file matches
1184 * the source.
1185 */
1186 if (strcmp(dstr, str) != 0) {
1187 devlink_db_fault(hdp, "write_string: post-write mismatch: "
1188 "wrote \"%s\", read back \"%s\"", str, dstr);
1189 }
1190
1191 DB(hdp)->str_crc = devlink_crc32(DB(hdp)->str_crc, str,
1192 strlen(str) + 1);
1193
1194 next[DB_STR] += strlen(dstr) + 1;
1195
1196 return (idx);
1197 }
1198
1199 static int
close_db(struct di_devlink_handle * hdp)1200 close_db(struct di_devlink_handle *hdp)
1201 {
1202 int i, rv = 0;
1203 size_t sz;
1204
1205 if (!DB_OPEN(hdp)) {
1206 #ifdef DEBUG
1207 assert(DB(hdp)->db_fd == -1);
1208 assert(DB(hdp)->flags == 0);
1209 for (i = 0; i < DB_TYPES; i++) {
1210 assert(DB_SEG(hdp, i) == NULL);
1211 assert(DB_SEG_PROT(hdp, i) == 0);
1212 }
1213 #endif
1214 return (0);
1215 }
1216
1217 /* Unmap header after unmapping all other mapped segments */
1218 for (i = 0; i < DB_TYPES; i++) {
1219 if (DB_SEG(hdp, i)) {
1220 sz = seg_size(hdp, i);
1221 if (DB_RDWR(hdp))
1222 rv += msync(DB_SEG(hdp, i), sz, MS_SYNC);
1223 (void) munmap(DB_SEG(hdp, i), sz);
1224 DB_SEG(hdp, i) = NULL;
1225 DB_SEG_PROT(hdp, i) = 0;
1226 }
1227 }
1228
1229 if (DB_RDWR(hdp))
1230 rv += msync((caddr_t)DB_HDR(hdp), HDR_LEN, MS_SYNC);
1231 (void) munmap((caddr_t)DB_HDR(hdp), HDR_LEN);
1232 DB(hdp)->hdr = NULL;
1233
1234 (void) close(DB(hdp)->db_fd);
1235 DB(hdp)->db_fd = -1;
1236 DB(hdp)->flags = 0;
1237
1238 return (rv ? -1 : 0);
1239 }
1240
1241
1242 static void
cache_free(struct di_devlink_handle * hdp)1243 cache_free(struct di_devlink_handle *hdp)
1244 {
1245 cache_link_t *clp;
1246
1247 subtree_free(hdp, &(CACHE_ROOT(hdp)));
1248 assert(CACHE_LAST(hdp) == NULL);
1249
1250 /*
1251 * Don't bother removing links from hash table chains,
1252 * as we are freeing the hash table itself.
1253 */
1254 while (CACHE(hdp)->dngl != NULL) {
1255 clp = CACHE(hdp)->dngl;
1256 CACHE(hdp)->dngl = clp->sib;
1257 assert(clp->minor == NULL);
1258 link_free(&clp);
1259 }
1260
1261 assert((CACHE(hdp)->hash == NULL) ^ (CACHE(hdp)->hash_sz != 0));
1262
1263 free(CACHE(hdp)->hash);
1264 CACHE(hdp)->hash = NULL;
1265 CACHE(hdp)->hash_sz = 0;
1266 }
1267
1268 static void
handle_free(struct di_devlink_handle ** pp)1269 handle_free(struct di_devlink_handle **pp)
1270 {
1271 struct di_devlink_handle *hdp = *pp;
1272
1273 *pp = NULL;
1274
1275 if (hdp == NULL)
1276 return;
1277
1278 (void) close_db(hdp);
1279 cache_free(hdp);
1280
1281 if (HDL_RDWR(hdp))
1282 exit_db_lock(hdp);
1283 assert(hdp->lock_fd == -1);
1284
1285 free(hdp->dev_dir);
1286 free(hdp->db_dir);
1287 free(hdp);
1288 }
1289
1290 /*
1291 * Frees the tree rooted at a node. Siblings of the subtree root
1292 * have to be handled by the caller.
1293 */
1294 static void
subtree_free(struct di_devlink_handle * hdp,cache_node_t ** pp)1295 subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp)
1296 {
1297 cache_node_t *np;
1298 cache_link_t *clp;
1299 cache_minor_t *cmnp;
1300
1301 if (pp == NULL || *pp == NULL)
1302 return;
1303
1304 while ((*pp)->child != NULL) {
1305 np = (*pp)->child;
1306 (*pp)->child = np->sib;
1307 subtree_free(hdp, &np);
1308 }
1309
1310 while ((*pp)->minor != NULL) {
1311 cmnp = (*pp)->minor;
1312 (*pp)->minor = cmnp->sib;
1313
1314 while (cmnp->link != NULL) {
1315 clp = cmnp->link;
1316 cmnp->link = clp->sib;
1317 rm_link_from_hash(hdp, clp);
1318 link_free(&clp);
1319 }
1320 minor_free(hdp, &cmnp);
1321 }
1322
1323 node_free(pp);
1324 }
1325
1326 static void
rm_link_from_hash(struct di_devlink_handle * hdp,cache_link_t * clp)1327 rm_link_from_hash(struct di_devlink_handle *hdp, cache_link_t *clp)
1328 {
1329 int hval;
1330 cache_link_t **pp;
1331
1332 if (clp == NULL)
1333 return;
1334
1335 if (clp->path == NULL)
1336 return;
1337
1338 hval = hashfn(hdp, clp->path);
1339 pp = &(CACHE_HASH(hdp, hval));
1340 for (; *pp != NULL; pp = &(*pp)->hash) {
1341 if (*pp == clp) {
1342 *pp = clp->hash;
1343 clp->hash = NULL;
1344 return;
1345 }
1346 }
1347
1348 devlink_dprintf(DBG_ERR, "rm_link_from_hash: link(%s) not found\n",
1349 clp->path);
1350 }
1351
1352 static cache_link_t *
link_hash(di_devlink_handle_t hdp,const char * link,uint_t flags)1353 link_hash(di_devlink_handle_t hdp, const char *link, uint_t flags)
1354 {
1355 int hval;
1356 cache_link_t **pp, *clp;
1357
1358 if (link == NULL)
1359 return (NULL);
1360
1361 hval = hashfn(hdp, link);
1362 pp = &(CACHE_HASH(hdp, hval));
1363 for (; (clp = *pp) != NULL; pp = &clp->hash) {
1364 if (strcmp(clp->path, link) == 0) {
1365 break;
1366 }
1367 }
1368
1369 if (clp == NULL)
1370 return (NULL);
1371
1372 if ((flags & UNLINK_FROM_HASH) == UNLINK_FROM_HASH) {
1373 *pp = clp->hash;
1374 clp->hash = NULL;
1375 }
1376
1377 return (clp);
1378 }
1379
1380 static cache_minor_t *
link2minor(struct di_devlink_handle * hdp,cache_link_t * clp)1381 link2minor(struct di_devlink_handle *hdp, cache_link_t *clp)
1382 {
1383 cache_link_t *plp;
1384 const char *minor_path;
1385 char *cp, buf[PATH_MAX], link[PATH_MAX];
1386 char abspath[PATH_MAX];
1387 struct stat st;
1388
1389 if (TYPE_PRI(attr2type(clp->attr))) {
1390 /*
1391 * For primary link, content should point to a /devices node.
1392 */
1393 if (!is_minor_node(clp->content, &minor_path)) {
1394 return (NULL);
1395 }
1396
1397 return (lookup_minor(hdp, minor_path, NULL,
1398 TYPE_CACHE|CREATE_FLAG));
1399
1400 }
1401
1402 /*
1403 * If secondary, the primary link is derived from the secondary
1404 * link contents. Secondary link contents can have two formats:
1405 * audio -> /dev/sound/0
1406 * fb0 -> fbs/afb0
1407 */
1408
1409 buf[0] = '\0';
1410 if (strncmp(clp->content, DEV"/", strlen(DEV"/")) == 0) {
1411 cp = &clp->content[strlen(DEV"/")];
1412 } else if (clp->content[0] != '/') {
1413 if ((cp = strrchr(clp->path, '/')) != NULL) {
1414 char savechar = *(cp + 1);
1415 *(cp + 1) = '\0';
1416 (void) snprintf(buf, sizeof (buf), "%s", clp->path);
1417 *(cp + 1) = savechar;
1418 }
1419 (void) strlcat(buf, clp->content, sizeof (buf));
1420 cp = buf;
1421 } else {
1422 goto follow_link;
1423 }
1424
1425 /*
1426 * Lookup the primary link if possible and find its minor.
1427 */
1428 if ((plp = link_hash(hdp, cp, 0)) != NULL && plp->minor != NULL) {
1429 return (plp->minor);
1430 }
1431
1432 /* realpath() used only as a last resort because it is expensive */
1433 follow_link:
1434 (void) snprintf(link, sizeof (link), "%s/%s", hdp->dev_dir, clp->path);
1435
1436 #ifdef DEBUG
1437 /*LINTED*/
1438 assert(sizeof (buf) >= PATH_MAX);
1439 #endif
1440
1441 /*
1442 * A realpath attempt to lookup a dangling link can invoke implicit
1443 * reconfig so verify there's an actual device behind the link first.
1444 */
1445 if (lstat(link, &st) == -1)
1446 return (NULL);
1447 if (S_ISLNK(st.st_mode)) {
1448 if (s_readlink(link, buf, sizeof (buf)) < 0)
1449 return (NULL);
1450 if (buf[0] != '/') {
1451 char *p;
1452 size_t n = sizeof (abspath);
1453 if (strlcpy(abspath, link, n) >= n)
1454 return (NULL);
1455 p = strrchr(abspath, '/') + 1;
1456 *p = 0;
1457 n = sizeof (abspath) - strlen(p);
1458 if (strlcpy(p, buf, n) >= n)
1459 return (NULL);
1460 } else {
1461 if (strlcpy(abspath, buf, sizeof (abspath)) >=
1462 sizeof (abspath))
1463 return (NULL);
1464 }
1465 if (!device_exists(abspath))
1466 return (NULL);
1467 }
1468
1469 if (s_realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) {
1470 return (NULL);
1471 }
1472 return (lookup_minor(hdp, minor_path, NULL, TYPE_CACHE|CREATE_FLAG));
1473 }
1474
1475
1476 static void
resolve_dangling_links(struct di_devlink_handle * hdp)1477 resolve_dangling_links(struct di_devlink_handle *hdp)
1478 {
1479 cache_minor_t *cmnp;
1480 cache_link_t *clp, **pp;
1481
1482 for (pp = &(CACHE(hdp)->dngl); *pp != NULL; ) {
1483 clp = *pp;
1484 if ((cmnp = link2minor(hdp, clp)) != NULL) {
1485 *pp = clp->sib;
1486 clp->sib = cmnp->link;
1487 cmnp->link = clp;
1488 assert(clp->minor == NULL);
1489 clp->minor = cmnp;
1490 } else {
1491 devlink_dprintf(DBG_INFO,
1492 "resolve_dangling_links: link(%s): unresolved\n",
1493 clp->path);
1494 pp = &clp->sib;
1495 }
1496 }
1497 }
1498
1499
1500 /*
1501 * The elements are assumed to be detached from the cache tree.
1502 */
1503 static void
node_free(cache_node_t ** pp)1504 node_free(cache_node_t **pp)
1505 {
1506 cache_node_t *cnp = *pp;
1507
1508 *pp = NULL;
1509
1510 if (cnp == NULL)
1511 return;
1512
1513 free(cnp->path);
1514 free(cnp);
1515 }
1516
1517 static void
minor_free(struct di_devlink_handle * hdp,cache_minor_t ** pp)1518 minor_free(struct di_devlink_handle *hdp, cache_minor_t **pp)
1519 {
1520 cache_minor_t *cmnp = *pp;
1521
1522 *pp = NULL;
1523
1524 if (cmnp == NULL)
1525 return;
1526
1527 if (CACHE_LAST(hdp) == cmnp) {
1528 devlink_dprintf(DBG_STEP, "minor_free: last_minor(%s)\n",
1529 cmnp->name);
1530 CACHE_LAST(hdp) = NULL;
1531 }
1532
1533 free(cmnp->name);
1534 free(cmnp->nodetype);
1535 free(cmnp);
1536 }
1537
1538 static void
link_free(cache_link_t ** pp)1539 link_free(cache_link_t **pp)
1540 {
1541 cache_link_t *clp = *pp;
1542
1543 *pp = NULL;
1544
1545 if (clp == NULL)
1546 return;
1547
1548 free(clp->path);
1549 free(clp->content);
1550 free(clp);
1551 }
1552
1553 /*
1554 * Returns the ':' preceding the minor name
1555 */
1556 static char *
minor_colon(const char * path)1557 minor_colon(const char *path)
1558 {
1559 char *cp;
1560
1561 if ((cp = strrchr(path, '/')) == NULL) {
1562 return (NULL);
1563 }
1564
1565 return (strchr(cp, ':'));
1566 }
1567
1568 static void *
lookup_minor(struct di_devlink_handle * hdp,const char * minor_path,const char * nodetype,const int flags)1569 lookup_minor(
1570 struct di_devlink_handle *hdp,
1571 const char *minor_path,
1572 const char *nodetype,
1573 const int flags)
1574 {
1575 void *vp;
1576 char *colon;
1577 char pdup[PATH_MAX];
1578 const char *fcn = "lookup_minor";
1579
1580 if (minor_path == NULL) {
1581 errno = EINVAL;
1582 return (NULL);
1583 }
1584
1585 (void) snprintf(pdup, sizeof (pdup), "%s", minor_path);
1586
1587 if ((colon = minor_colon(pdup)) == NULL) {
1588 (void) devlink_dprintf(DBG_ERR, "%s: invalid minor path(%s)\n",
1589 fcn, minor_path);
1590 errno = EINVAL;
1591 return (NULL);
1592 }
1593 *colon = '\0';
1594
1595 if ((vp = get_last_minor(hdp, pdup, colon + 1, flags)) != NULL) {
1596 return (vp);
1597 }
1598
1599 if ((vp = lookup_node(hdp, pdup, flags)) == NULL) {
1600 (void) devlink_dprintf(DBG_ERR, "%s: node(%s) not found\n",
1601 fcn, pdup);
1602 return (NULL);
1603 }
1604 *colon = ':';
1605
1606 if (LOOKUP_CACHE(flags)) {
1607 cache_minor_t **pp;
1608
1609 pp = &((cache_node_t *)vp)->minor;
1610 for (; *pp != NULL; pp = &(*pp)->sib) {
1611 if (strcmp((*pp)->name, colon + 1) == 0)
1612 break;
1613 }
1614
1615 if (*pp == NULL && CREATE_ELEM(flags)) {
1616 *pp = minor_insert(hdp, vp, colon + 1, nodetype, pp);
1617 }
1618 set_last_minor(hdp, *pp, flags);
1619
1620 return (*pp);
1621 } else {
1622 char *cp;
1623 uint32_t nidx;
1624 struct db_minor *dmp;
1625
1626 nidx = (((struct db_node *)vp)->minor);
1627 for (; (dmp = get_minor(hdp, nidx)) != NULL; nidx = dmp->sib) {
1628 cp = get_string(hdp, dmp->name);
1629 if (cp && strcmp(cp, colon + 1) == 0)
1630 break;
1631 }
1632 return (dmp);
1633 }
1634 }
1635
1636 static void *
lookup_node(struct di_devlink_handle * hdp,char * path,const int flags)1637 lookup_node(struct di_devlink_handle *hdp, char *path, const int flags)
1638 {
1639 struct tnode tnd = {NULL};
1640
1641 if ((tnd.node = get_last_node(hdp, path, flags)) != NULL)
1642 return (tnd.node);
1643
1644 tnd.handle = hdp;
1645 tnd.flags = flags;
1646
1647 if (walk_tree(path, &tnd, visit_node) != 0)
1648 return (NULL);
1649
1650 return (tnd.node);
1651 }
1652
1653 /*
1654 * last_minor is used for nodes of TYPE_CACHE only.
1655 */
1656 static void *
get_last_node(struct di_devlink_handle * hdp,const char * path,int flags)1657 get_last_node(struct di_devlink_handle *hdp, const char *path, int flags)
1658 {
1659 cache_node_t *cnp;
1660
1661 #ifdef DEBUG
1662 if (getenv(SKIP_LAST_CACHE)) {
1663 (void) devlink_dprintf(DBG_INFO,
1664 "get_last_node: SKIPPING \"last\" node cache\n");
1665 return (NULL);
1666 }
1667 #endif
1668
1669 if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL ||
1670 CACHE_LAST(hdp)->node == NULL) {
1671 return (NULL);
1672 }
1673
1674 cnp = CACHE_LAST(hdp)->node;
1675 if (strcmp(cnp->path, path) == 0) {
1676 return (cnp);
1677 }
1678
1679 cnp = cnp->sib;
1680 if (cnp && strcmp(cnp->path, path) == 0) {
1681 return (cnp);
1682 }
1683
1684 return (NULL);
1685 }
1686
1687 static void *
get_last_minor(struct di_devlink_handle * hdp,const char * devfs_path,const char * minor_name,int flags)1688 get_last_minor(
1689 struct di_devlink_handle *hdp,
1690 const char *devfs_path,
1691 const char *minor_name,
1692 int flags)
1693 {
1694 cache_minor_t *cmnp;
1695
1696 #ifdef DEBUG
1697 if (getenv(SKIP_LAST_CACHE)) {
1698 (void) devlink_dprintf(DBG_INFO,
1699 "get_last_minor: SKIPPING \"last\" minor cache\n");
1700 return (NULL);
1701 }
1702 #endif
1703
1704 if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL) {
1705 return (NULL);
1706 }
1707
1708 cmnp = CACHE_LAST(hdp);
1709 if (strcmp(cmnp->name, minor_name) == 0 && cmnp->node &&
1710 strcmp(cmnp->node->path, devfs_path) == 0) {
1711 return (cmnp);
1712 }
1713
1714 cmnp = cmnp->sib;
1715 if (cmnp && strcmp(cmnp->name, minor_name) == 0 && cmnp->node &&
1716 strcmp(cmnp->node->path, devfs_path) == 0) {
1717 set_last_minor(hdp, cmnp, TYPE_CACHE);
1718 return (cmnp);
1719 }
1720
1721 return (NULL);
1722 }
1723
1724 static void
set_last_minor(struct di_devlink_handle * hdp,cache_minor_t * cmnp,int flags)1725 set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp, int flags)
1726 {
1727 #ifdef DEBUG
1728 if (getenv(SKIP_LAST_CACHE)) {
1729 (void) devlink_dprintf(DBG_INFO,
1730 "set_last_minor: SKIPPING \"last\" minor cache\n");
1731 return;
1732 }
1733 #endif
1734
1735 if (LOOKUP_CACHE(flags) && cmnp) {
1736 CACHE_LAST(hdp) = cmnp;
1737 }
1738 }
1739
1740
1741 /*
1742 * Returns 0 if normal return or -1 otherwise.
1743 */
1744 static int
walk_tree(char * cur,void * arg,int (* node_callback)(const char * path,void * arg))1745 walk_tree(
1746 char *cur,
1747 void *arg,
1748 int (*node_callback)(const char *path, void *arg))
1749 {
1750 char *slash, buf[PATH_MAX];
1751
1752 if (cur == NULL || cur[0] != '/' || strlen(cur) > sizeof (buf) - 1) {
1753 errno = EINVAL;
1754 return (-1);
1755 }
1756
1757 (void) strcpy(buf, "/");
1758
1759 for (;;) {
1760
1761 if (node_callback(buf, arg) != DI_WALK_CONTINUE)
1762 break;
1763
1764 while (*cur == '/')
1765 cur++;
1766
1767 if (*cur == '\0')
1768 break;
1769
1770 /*
1771 * There is a next component(s). Append a "/" separator for all
1772 * but the first (root) component.
1773 */
1774 if (buf[1] != '\0') {
1775 (void) strlcat(buf, "/", sizeof (buf));
1776 }
1777
1778 if ((slash = strchr(cur, '/')) != NULL) {
1779 *slash = '\0';
1780 (void) strlcat(buf, cur, sizeof (buf));
1781 *slash = '/';
1782 cur = slash;
1783 } else {
1784 (void) strlcat(buf, cur, sizeof (buf));
1785 cur += strlen(cur);
1786 }
1787
1788 }
1789
1790 return (0);
1791 }
1792
1793
1794 static int
visit_node(const char * path,void * arg)1795 visit_node(const char *path, void *arg)
1796 {
1797 struct tnode *tnp = arg;
1798
1799 if (LOOKUP_CACHE(tnp->flags)) {
1800
1801 cache_node_t *cnp = tnp->node;
1802
1803 cnp = (cnp) ? cnp->child : CACHE_ROOT(tnp->handle);
1804
1805 for (; cnp != NULL; cnp = cnp->sib) {
1806 if (strcmp(cnp->path, path) == 0)
1807 break;
1808 }
1809 if (cnp == NULL && CREATE_ELEM(tnp->flags)) {
1810 cnp = node_insert(tnp->handle, tnp->node, path,
1811 INSERT_TAIL);
1812 }
1813 tnp->node = cnp;
1814 } else {
1815 char *cp;
1816 struct db_node *dnp = tnp->node;
1817
1818 dnp = (dnp) ? get_node(tnp->handle, dnp->child)
1819 : get_node(tnp->handle, DB_HDR(tnp->handle)->root_idx);
1820
1821 for (; dnp != NULL; dnp = get_node(tnp->handle, dnp->sib)) {
1822 cp = get_string(tnp->handle, dnp->path);
1823 if (cp && strcmp(cp, path) == 0) {
1824 break;
1825 }
1826 }
1827 tnp->node = dnp;
1828 }
1829
1830 /*
1831 * Terminate walk if node is not found for a path component.
1832 */
1833 return (tnp->node ? DI_WALK_CONTINUE : DI_WALK_TERMINATE);
1834 }
1835
1836 static void
minor_delete(di_devlink_handle_t hdp,cache_minor_t * cmnp)1837 minor_delete(di_devlink_handle_t hdp, cache_minor_t *cmnp)
1838 {
1839 cache_link_t **lpp;
1840 cache_minor_t **mpp;
1841 const char *fcn = "minor_delete";
1842
1843 (void) devlink_dprintf(DBG_STEP, "%s: removing minor: %s\n",
1844 fcn, cmnp->name);
1845
1846 /* detach minor from node */
1847 if (cmnp->node != NULL) {
1848 mpp = &cmnp->node->minor;
1849 for (; *mpp != NULL; mpp = &(*mpp)->sib) {
1850 if (*mpp == cmnp)
1851 break;
1852 }
1853
1854 if (*mpp == NULL) {
1855 (void) devlink_dprintf(DBG_ERR,
1856 "%s: dangling minor: %s\n", fcn, cmnp->name);
1857 } else {
1858 *mpp = cmnp->sib;
1859 }
1860 } else {
1861 (void) devlink_dprintf(DBG_ERR, "%s: orphan minor(%s)\n", fcn,
1862 cmnp->name);
1863 }
1864
1865 delete_unused_nodes(hdp, cmnp->node);
1866
1867 cmnp->node = NULL;
1868 cmnp->sib = NULL;
1869
1870 /* Move all remaining links to dangling list */
1871 for (lpp = &cmnp->link; *lpp != NULL; lpp = &(*lpp)->sib) {
1872 (*lpp)->minor = NULL;
1873 }
1874 *lpp = CACHE(hdp)->dngl;
1875 CACHE(hdp)->dngl = cmnp->link;
1876 cmnp->link = NULL;
1877
1878 minor_free(hdp, &cmnp);
1879 }
1880
1881 static void
delete_unused_nodes(di_devlink_handle_t hdp,cache_node_t * cnp)1882 delete_unused_nodes(di_devlink_handle_t hdp, cache_node_t *cnp)
1883 {
1884 cache_node_t **npp;
1885 const char *fcn = "delete_unused_nodes";
1886
1887 if (cnp == NULL)
1888 return;
1889
1890 if (cnp->minor != NULL || cnp->child != NULL)
1891 return;
1892
1893 (void) devlink_dprintf(DBG_INFO, "%s: removing unused node: %s\n", fcn,
1894 cnp->path);
1895
1896 /* Unlink node from tree */
1897 if (cnp->parent != NULL) {
1898 npp = &cnp->parent->child;
1899 for (; *npp != NULL; npp = &(*npp)->sib) {
1900 if (*npp == cnp)
1901 break;
1902 }
1903
1904 if (*npp == NULL) {
1905 (void) devlink_dprintf(DBG_ERR,
1906 "%s: dangling node: %s\n", fcn, cnp->path);
1907 } else {
1908 *npp = cnp->sib;
1909 }
1910 } else if (cnp == CACHE_ROOT(hdp)) {
1911 CACHE_ROOT(hdp) = NULL;
1912 } else {
1913 (void) devlink_dprintf(DBG_ERR, "%s: orphan node (%s)\n", fcn,
1914 cnp->path);
1915 }
1916
1917 delete_unused_nodes(hdp, cnp->parent);
1918
1919 cnp->parent = cnp->sib = NULL;
1920
1921 node_free(&cnp);
1922 }
1923
1924 static int
rm_link(di_devlink_handle_t hdp,const char * link)1925 rm_link(di_devlink_handle_t hdp, const char *link)
1926 {
1927 cache_link_t *clp;
1928 const char *fcn = "rm_link";
1929
1930 if (hdp == NULL || DB_ERR(hdp) || link == NULL || link[0] == '/' ||
1931 (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) {
1932 devlink_dprintf(DBG_ERR, "%s: %s: invalid args\n",
1933 fcn, link ? link : "<NULL>");
1934 errno = EINVAL;
1935 return (-1);
1936 }
1937
1938 devlink_dprintf(DBG_STEP, "%s: link(%s)\n", fcn, link);
1939
1940 if ((clp = link_hash(hdp, link, UNLINK_FROM_HASH)) == NULL) {
1941 return (0);
1942 }
1943
1944 link_delete(hdp, clp);
1945
1946 return (0);
1947 }
1948
1949 int
di_devlink_rm_link(di_devlink_handle_t hdp,const char * link)1950 di_devlink_rm_link(di_devlink_handle_t hdp, const char *link)
1951 {
1952 if (hdp == NULL || !HDL_RDWR(hdp)) {
1953 errno = EINVAL;
1954 return (-1);
1955 }
1956
1957 return (rm_link(hdp, link));
1958 }
1959
1960 static void
link_delete(di_devlink_handle_t hdp,cache_link_t * clp)1961 link_delete(di_devlink_handle_t hdp, cache_link_t *clp)
1962 {
1963 cache_link_t **pp;
1964 const char *fcn = "link_delete";
1965
1966 (void) devlink_dprintf(DBG_STEP, "%s: removing link: %s\n",
1967 fcn, clp->path);
1968
1969 if (clp->minor == NULL)
1970 pp = &(CACHE(hdp)->dngl);
1971 else
1972 pp = &clp->minor->link;
1973
1974 for (; *pp != NULL; pp = &(*pp)->sib) {
1975 if (*pp == clp)
1976 break;
1977 }
1978
1979 if (*pp == NULL) {
1980 (void) devlink_dprintf(DBG_ERR, "%s: link(%s) not on list\n",
1981 fcn, clp->path);
1982 } else {
1983 *pp = clp->sib;
1984 }
1985
1986 delete_unused_minor(hdp, clp->minor);
1987
1988 clp->minor = NULL;
1989
1990 link_free(&clp);
1991 }
1992
1993 static void
delete_unused_minor(di_devlink_handle_t hdp,cache_minor_t * cmnp)1994 delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp)
1995 {
1996 if (cmnp == NULL)
1997 return;
1998
1999 if (cmnp->link != NULL)
2000 return;
2001
2002 devlink_dprintf(DBG_STEP, "delete_unused_minor: removing minor(%s)\n",
2003 cmnp->name);
2004
2005 minor_delete(hdp, cmnp);
2006 }
2007
2008 int
di_devlink_add_link(di_devlink_handle_t hdp,const char * link,const char * content,int flags)2009 di_devlink_add_link(
2010 di_devlink_handle_t hdp,
2011 const char *link,
2012 const char *content,
2013 int flags)
2014 {
2015 return (add_link(hdp, link, content, flags) != NULL ? 0 : -1);
2016 }
2017
2018 static cache_link_t *
add_link(struct di_devlink_handle * hdp,const char * link,const char * content,int flags)2019 add_link(
2020 struct di_devlink_handle *hdp,
2021 const char *link,
2022 const char *content,
2023 int flags)
2024 {
2025 uint32_t attr;
2026 cache_link_t *clp;
2027 cache_minor_t *cmnp;
2028 const char *fcn = "add_link";
2029
2030 if (hdp == NULL || DB_ERR(hdp) || link == NULL ||
2031 link[0] == '/' || content == NULL || !link_flag(flags) ||
2032 (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) {
2033 devlink_dprintf(DBG_ERR, "%s: %s: invalid args\n",
2034 fcn, link ? link : "<NULL>");
2035 errno = EINVAL;
2036 return (NULL);
2037 }
2038
2039 if ((clp = link_hash(hdp, link, 0)) != NULL) {
2040 if (link_cmp(clp, content, LINK_TYPE(flags)) != 0) {
2041 (void) rm_link(hdp, link);
2042 } else {
2043 return (clp);
2044 }
2045 }
2046
2047 if (TYPE_PRI(flags)) {
2048 const char *minor_path = NULL;
2049
2050 if (!is_minor_node(content, &minor_path)) {
2051 (void) devlink_dprintf(DBG_ERR,
2052 "%s: invalid content(%s) for primary link\n",
2053 fcn, content);
2054 errno = EINVAL;
2055 return (NULL);
2056 }
2057 if ((cmnp = lookup_minor(hdp, minor_path, NULL,
2058 TYPE_CACHE|CREATE_FLAG)) == NULL) {
2059 return (NULL);
2060 }
2061 attr = A_PRIMARY;
2062 } else {
2063 /*
2064 * Defer resolving a secondary link to a minor until the
2065 * database is closed. This ensures that the primary link
2066 * (required for a successful resolve) has also been created.
2067 */
2068 cmnp = NULL;
2069 attr = A_SECONDARY;
2070 }
2071
2072 return (link_insert(hdp, cmnp, link, content, attr));
2073 }
2074
2075 /*
2076 * Returns 0 on match or 1 otherwise.
2077 */
2078 static int
link_cmp(cache_link_t * clp,const char * content,int type)2079 link_cmp(cache_link_t *clp, const char *content, int type)
2080 {
2081 if (strcmp(clp->content, content) != 0)
2082 return (1);
2083
2084 if (attr2type(clp->attr) != type)
2085 return (1);
2086
2087 return (0);
2088 }
2089
2090 int
di_devlink_update(di_devlink_handle_t hdp)2091 di_devlink_update(di_devlink_handle_t hdp)
2092 {
2093 if (hdp == NULL || !HDL_RDWR(hdp) || DB_ERR(hdp)) {
2094 errno = EINVAL;
2095 return (-1);
2096 }
2097
2098 /*
2099 * Reset the counter to schedule a synchronization with /dev on the next
2100 * di_devlink_close().
2101 */
2102 CACHE(hdp)->update_count = 0;
2103
2104 return (0);
2105 }
2106
2107 static int
synchronize_db(di_devlink_handle_t hdp)2108 synchronize_db(di_devlink_handle_t hdp)
2109 {
2110 int hval;
2111 cache_link_t *clp;
2112 char pdup[PATH_MAX];
2113 recurse_t rec = {NULL};
2114 const char *fcn = "synchronize_db";
2115
2116 rec.data = NULL;
2117 rec.fcn = cache_dev_link;
2118
2119 /*
2120 * Walk through $ROOT/dev, reading every link and marking the
2121 * corresponding cached version as valid(adding new links as needed).
2122 * Then walk through the cache and remove all unmarked links.
2123 */
2124 if (recurse_dev(hdp, &rec) != 0) {
2125 return (-1);
2126 }
2127
2128 for (hval = 0; hval < CACHE(hdp)->hash_sz; hval++) {
2129 for (clp = CACHE_HASH(hdp, hval); clp != NULL; ) {
2130 if (GET_VALID_ATTR(clp->attr)) {
2131 CLR_VALID_ATTR(clp->attr);
2132 clp = clp->hash;
2133 continue;
2134 }
2135
2136 /*
2137 * The link is stale, so remove it. Since the link
2138 * will be destroyed, use a copy of the link path to
2139 * invoke the remove function.
2140 */
2141 (void) snprintf(pdup, sizeof (pdup), "%s", clp->path);
2142 clp = clp->hash;
2143 (void) devlink_dprintf(DBG_STEP,
2144 "%s: removing invalid link: %s\n", fcn, pdup);
2145 (void) di_devlink_rm_link(hdp, pdup);
2146 }
2147 }
2148
2149 (void) devlink_dprintf(DBG_STEP, "%s: update completed\n", fcn);
2150
2151 return (0);
2152 }
2153
2154 static di_devlink_handle_t
di_devlink_init_impl(const char * root,const char * name,uint_t flags)2155 di_devlink_init_impl(const char *root, const char *name, uint_t flags)
2156 {
2157 int err = 0;
2158
2159 if ((flags != 0 && flags != DI_MAKE_LINK) ||
2160 (flags == 0 && name != NULL)) {
2161 errno = EINVAL;
2162 return (NULL);
2163 }
2164
2165 if ((flags == DI_MAKE_LINK) &&
2166 (err = devlink_create(root, name, DCA_DEVLINK_CACHE))) {
2167 errno = err;
2168 return (NULL);
2169 }
2170
2171 (void) devlink_dprintf(DBG_INFO, "devlink_init_impl: success\n");
2172
2173 return (devlink_snapshot(root));
2174 }
2175
2176 di_devlink_handle_t
di_devlink_init(const char * name,uint_t flags)2177 di_devlink_init(const char *name, uint_t flags)
2178 {
2179 return (di_devlink_init_impl("/", name, flags));
2180 }
2181
2182 di_devlink_handle_t
di_devlink_init_root(const char * root,const char * name,uint_t flags)2183 di_devlink_init_root(const char *root, const char *name, uint_t flags)
2184 {
2185 return (di_devlink_init_impl(root, name, flags));
2186 }
2187
2188 static di_devlink_handle_t
devlink_snapshot(const char * root_dir)2189 devlink_snapshot(const char *root_dir)
2190 {
2191 struct di_devlink_handle *hdp;
2192 int err;
2193 static int retried = 0;
2194
2195 if ((hdp = handle_alloc(root_dir, OPEN_RDONLY)) == NULL) {
2196 return (NULL);
2197 }
2198
2199 /*
2200 * We don't need to lock. If a consumer wants the very latest db
2201 * then it must perform a di_devlink_init with the DI_MAKE_LINK
2202 * flag to force a sync with devfsadm first. Otherwise, the
2203 * current database file is opened and mmaped on demand: the rename
2204 * associated with a db update does not change the contents
2205 * of files already opened.
2206 */
2207 again: err = open_db(hdp, OPEN_RDONLY);
2208
2209 /*
2210 * If we failed to open DB the most likely cause is that DB file did
2211 * not exist. If we have not done a retry, signal devfsadmd to
2212 * recreate the DB file and retry. If we fail to open the DB after
2213 * retry, we will walk /dev in di_devlink_walk.
2214 */
2215 if (err && (retried == 0)) {
2216 retried++;
2217 (void) devlink_create(root_dir, NULL, DCA_DEVLINK_SYNC);
2218 goto again;
2219 }
2220 return (hdp);
2221 }
2222
2223 int
di_devlink_fini(di_devlink_handle_t * pp)2224 di_devlink_fini(di_devlink_handle_t *pp)
2225 {
2226 if (pp == NULL || *pp == NULL || !HDL_RDONLY(*pp)) {
2227 errno = EINVAL;
2228 return (-1);
2229 }
2230
2231 /* Freeing the handle also closes the DB */
2232 handle_free(pp);
2233
2234 return (0);
2235 }
2236
2237 int
di_devlink_walk(di_devlink_handle_t hdp,const char * re,const char * minor_path,uint_t flags,void * arg,int (* devlink_callback)(di_devlink_t,void *))2238 di_devlink_walk(
2239 di_devlink_handle_t hdp,
2240 const char *re,
2241 const char *minor_path,
2242 uint_t flags,
2243 void *arg,
2244 int (*devlink_callback)(di_devlink_t, void *))
2245 {
2246 int rv;
2247 regex_t reg;
2248 link_desc_t linkd = {NULL};
2249
2250 if (hdp == NULL || !HDL_RDONLY(hdp)) {
2251 errno = EINVAL;
2252 return (-1);
2253 }
2254
2255 linkd.minor_path = minor_path;
2256 linkd.flags = flags;
2257 linkd.arg = arg;
2258 linkd.fcn = devlink_callback;
2259
2260 if (re) {
2261 if (regcomp(®, re, REG_EXTENDED) != 0)
2262 return (-1);
2263 linkd.regp = ®
2264 }
2265
2266 if (check_args(&linkd)) {
2267 errno = EINVAL;
2268 rv = -1;
2269 goto out;
2270 }
2271
2272 if (DB_OPEN(hdp)) {
2273 rv = walk_db(hdp, &linkd);
2274 } else {
2275 rv = walk_dev(hdp, &linkd);
2276 }
2277
2278 out:
2279 if (re) {
2280 regfree(®);
2281 }
2282
2283 return (rv ? -1 : 0);
2284 }
2285
2286 static int
link_flag(uint_t flags)2287 link_flag(uint_t flags)
2288 {
2289 if (flags != 0 && flags != DI_PRIMARY_LINK &&
2290 flags != DI_SECONDARY_LINK) {
2291 return (0);
2292 }
2293
2294 return (1);
2295 }
2296
2297 /*
2298 * Currently allowed flags are:
2299 * DI_PRIMARY_LINK
2300 * DI_SECONDARY_LINK
2301 */
2302 static int
check_args(link_desc_t * linkp)2303 check_args(link_desc_t *linkp)
2304 {
2305 if (linkp->fcn == NULL)
2306 return (-1);
2307
2308 if (!link_flag(linkp->flags)) {
2309 return (-1);
2310 }
2311
2312 /*
2313 * Minor path can be NULL. In that case, all links will be
2314 * selected.
2315 */
2316 if (linkp->minor_path) {
2317 if (linkp->minor_path[0] != '/' ||
2318 minor_colon(linkp->minor_path) == NULL) {
2319 return (-1);
2320 }
2321 }
2322
2323 return (0);
2324 }
2325
2326
2327 /*
2328 * Walk all links in database if no minor path is specified.
2329 */
2330 static int
walk_db(struct di_devlink_handle * hdp,link_desc_t * linkp)2331 walk_db(struct di_devlink_handle *hdp, link_desc_t *linkp)
2332 {
2333 assert(DB_OPEN(hdp));
2334
2335 if (linkp->minor_path == NULL) {
2336 return (walk_all_links(hdp, linkp));
2337 } else {
2338 return (walk_matching_links(hdp, linkp));
2339 }
2340 }
2341
2342 static int
cache_dev(struct di_devlink_handle * hdp)2343 cache_dev(struct di_devlink_handle *hdp)
2344 {
2345 size_t sz;
2346 recurse_t rec = {NULL};
2347
2348 assert(hdp);
2349 assert(HDL_RDONLY(hdp));
2350
2351 if (hdp == NULL || !HDL_RDONLY(hdp)) {
2352 devlink_dprintf(DBG_ERR, "cache_dev: invalid arg\n");
2353 return (-1);
2354 }
2355
2356 sz = MIN_HASH_SIZE;
2357
2358 CACHE(hdp)->hash = calloc(sz, sizeof (cache_link_t *));
2359 if (CACHE(hdp)->hash == NULL) {
2360 return (-1);
2361 }
2362 CACHE(hdp)->hash_sz = sz;
2363
2364 rec.data = NULL;
2365 rec.fcn = cache_dev_link;
2366
2367 return (recurse_dev(hdp, &rec));
2368 }
2369
2370 static int
walk_dev(struct di_devlink_handle * hdp,link_desc_t * linkp)2371 walk_dev(struct di_devlink_handle *hdp, link_desc_t *linkp)
2372 {
2373 assert(hdp && linkp);
2374 assert(!DB_OPEN(hdp));
2375 assert(HDL_RDONLY(hdp));
2376
2377 if (hdp == NULL || !HDL_RDONLY(hdp) || DB_OPEN(hdp)) {
2378 devlink_dprintf(DBG_ERR, "walk_dev: invalid args\n");
2379 return (-1);
2380 }
2381
2382 if (CACHE_EMPTY(hdp) && cache_dev(hdp) != 0) {
2383 devlink_dprintf(DBG_ERR, "walk_dev: /dev caching failed\n");
2384 return (-1);
2385 }
2386
2387 if (linkp->minor_path)
2388 walk_cache_minor(hdp, linkp->minor_path, linkp);
2389 else
2390 walk_all_cache(hdp, linkp);
2391
2392 return (linkp->retval);
2393 }
2394
2395 /* ARGSUSED */
2396 static int
cache_dev_link(struct di_devlink_handle * hdp,void * data,const char * link)2397 cache_dev_link(struct di_devlink_handle *hdp, void *data, const char *link)
2398 {
2399 int flags;
2400 cache_link_t *clp;
2401 char content[PATH_MAX];
2402
2403 assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp));
2404
2405 if (s_readlink(link, content, sizeof (content)) < 0) {
2406 return (DI_WALK_CONTINUE);
2407 }
2408
2409 if (is_minor_node(content, NULL)) {
2410 flags = DI_PRIMARY_LINK;
2411 } else {
2412 flags = DI_SECONDARY_LINK;
2413 }
2414
2415 assert(strncmp(link, hdp->dev_dir, strlen(hdp->dev_dir)) == 0);
2416
2417 /*
2418 * Store only the part after <root-dir>/dev/
2419 */
2420 link += strlen(hdp->dev_dir) + 1;
2421
2422 if ((clp = add_link(hdp, link, content, flags)) != NULL) {
2423 SET_VALID_ATTR(clp->attr);
2424 }
2425
2426 return (DI_WALK_CONTINUE);
2427 }
2428
2429
2430 static int
walk_all_links(struct di_devlink_handle * hdp,link_desc_t * linkp)2431 walk_all_links(struct di_devlink_handle *hdp, link_desc_t *linkp)
2432 {
2433 struct db_link *dlp;
2434 uint32_t nidx, eidx;
2435
2436 assert(DB_NUM(hdp, DB_LINK) >= 1);
2437
2438 eidx = DB_NUM(hdp, DB_LINK);
2439
2440 /* Skip the "NIL" (index == 0) link. */
2441 for (nidx = 1; nidx < eidx; nidx++) {
2442 /*
2443 * Declare this local to the block with zero
2444 * initializer so that it gets rezeroed
2445 * for each iteration.
2446 */
2447 struct di_devlink vlink = {NULL};
2448
2449 if ((dlp = get_link(hdp, nidx)) == NULL)
2450 continue;
2451
2452 vlink.rel_path = get_string(hdp, dlp->path);
2453 vlink.content = get_string(hdp, dlp->content);
2454 vlink.type = attr2type(dlp->attr);
2455
2456 if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE) {
2457 break;
2458 }
2459 }
2460
2461 return (linkp->retval);
2462 }
2463
2464 static int
walk_matching_links(struct di_devlink_handle * hdp,link_desc_t * linkp)2465 walk_matching_links(struct di_devlink_handle *hdp, link_desc_t *linkp)
2466 {
2467 uint32_t nidx;
2468 struct db_link *dlp;
2469 struct db_minor *dmp;
2470
2471 assert(linkp->minor_path != NULL);
2472
2473 dmp = lookup_minor(hdp, linkp->minor_path, NULL, TYPE_DB);
2474
2475 /*
2476 * If a minor matching the path exists, walk that minor's devlinks list.
2477 * Then walk the dangling devlinks list. Non-matching devlinks will be
2478 * filtered out in visit_link.
2479 */
2480 for (;;) {
2481 nidx = dmp ? dmp->link : DB_HDR(hdp)->dngl_idx;
2482 for (; (dlp = get_link(hdp, nidx)) != NULL; nidx = dlp->sib) {
2483 struct di_devlink vlink = {NULL};
2484
2485 vlink.rel_path = get_string(hdp, dlp->path);
2486 vlink.content = get_string(hdp, dlp->content);
2487 vlink.type = attr2type(dlp->attr);
2488
2489 if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE)
2490 goto out;
2491 }
2492 if (dmp == NULL) {
2493 break;
2494 } else {
2495 dmp = NULL;
2496 }
2497 }
2498
2499 out:
2500 return (linkp->retval);
2501 }
2502
2503 static int
visit_link(struct di_devlink_handle * hdp,link_desc_t * linkp,struct di_devlink * vlp)2504 visit_link(
2505 struct di_devlink_handle *hdp,
2506 link_desc_t *linkp,
2507 struct di_devlink *vlp)
2508 {
2509 struct stat sbuf;
2510 const char *minor_path = NULL;
2511 char abs_path[PATH_MAX], cont[PATH_MAX];
2512
2513 /*
2514 * It is legal for the link's content and type to be unknown.
2515 * but one of absolute or relative path must be set.
2516 */
2517 if (vlp->rel_path == NULL && vlp->abs_path == NULL) {
2518 (void) devlink_dprintf(DBG_ERR,
2519 "visit_link: invalid arguments\n");
2520 return (DI_WALK_CONTINUE);
2521 }
2522
2523 if (vlp->rel_path == NULL) {
2524 vlp->rel_path = (char *)rel_path(hdp, vlp->abs_path);
2525 if (vlp->rel_path == NULL || vlp->rel_path[0] == '\0')
2526 return (DI_WALK_CONTINUE);
2527 }
2528
2529 if (linkp->regp) {
2530 if (regexec(linkp->regp, vlp->rel_path, 0, NULL, 0) != 0)
2531 return (DI_WALK_CONTINUE);
2532 }
2533
2534 if (vlp->abs_path == NULL) {
2535 assert(vlp->rel_path[0] != '/');
2536 (void) snprintf(abs_path, sizeof (abs_path), "%s/%s",
2537 hdp->dev_dir, vlp->rel_path);
2538 vlp->abs_path = abs_path;
2539 }
2540
2541 if (vlp->content == NULL) {
2542 if (s_readlink(vlp->abs_path, cont, sizeof (cont)) < 0) {
2543 return (DI_WALK_CONTINUE);
2544 }
2545 vlp->content = cont;
2546 }
2547
2548
2549 if (vlp->type == 0) {
2550 if (is_minor_node(vlp->content, &minor_path)) {
2551 vlp->type = DI_PRIMARY_LINK;
2552 } else {
2553 vlp->type = DI_SECONDARY_LINK;
2554 }
2555 }
2556
2557 /*
2558 * Filter based on minor path
2559 */
2560 if (linkp->minor_path) {
2561 char tmp[PATH_MAX];
2562
2563 /*
2564 * derive minor path
2565 */
2566 if (vlp->type == DI_SECONDARY_LINK) {
2567
2568 #ifdef DEBUG
2569 /*LINTED*/
2570 assert(sizeof (tmp) >= PATH_MAX);
2571 #endif
2572 if (s_realpath(vlp->abs_path, tmp) == NULL)
2573 return (DI_WALK_CONTINUE);
2574
2575 if (!is_minor_node(tmp, &minor_path))
2576 return (DI_WALK_CONTINUE);
2577
2578 } else if (minor_path == NULL) {
2579 if (!is_minor_node(vlp->content, &minor_path))
2580 return (DI_WALK_CONTINUE);
2581 }
2582
2583 assert(minor_path != NULL);
2584
2585 if (strcmp(linkp->minor_path, minor_path) != 0)
2586 return (DI_WALK_CONTINUE);
2587 }
2588
2589 /*
2590 * Filter based on link type
2591 */
2592 if (!TYPE_NONE(linkp->flags) && LINK_TYPE(linkp->flags) != vlp->type) {
2593 return (DI_WALK_CONTINUE);
2594 }
2595
2596 if (lstat(vlp->abs_path, &sbuf) < 0) {
2597 devlink_dprintf(DBG_ERR, "visit_link: %s: lstat failed: %s\n",
2598 vlp->abs_path, strerror(errno));
2599 return (DI_WALK_CONTINUE);
2600 }
2601
2602 return (linkp->fcn(vlp, linkp->arg));
2603 }
2604
2605 static int
devlink_valid(di_devlink_t devlink)2606 devlink_valid(di_devlink_t devlink)
2607 {
2608 if (devlink == NULL || devlink->rel_path == NULL ||
2609 devlink->abs_path == NULL || devlink->content == NULL ||
2610 TYPE_NONE(devlink->type)) {
2611 return (0);
2612 }
2613
2614 return (1);
2615 }
2616
2617 const char *
di_devlink_path(di_devlink_t devlink)2618 di_devlink_path(di_devlink_t devlink)
2619 {
2620 if (!devlink_valid(devlink)) {
2621 errno = EINVAL;
2622 return (NULL);
2623 }
2624
2625 return (devlink->abs_path);
2626 }
2627
2628 const char *
di_devlink_content(di_devlink_t devlink)2629 di_devlink_content(di_devlink_t devlink)
2630 {
2631 if (!devlink_valid(devlink)) {
2632 errno = EINVAL;
2633 return (NULL);
2634 }
2635
2636 return (devlink->content);
2637 }
2638
2639 int
di_devlink_type(di_devlink_t devlink)2640 di_devlink_type(di_devlink_t devlink)
2641 {
2642 if (!devlink_valid(devlink)) {
2643 errno = EINVAL;
2644 return (-1);
2645 }
2646
2647 return (devlink->type);
2648 }
2649
2650 di_devlink_t
di_devlink_dup(di_devlink_t devlink)2651 di_devlink_dup(di_devlink_t devlink)
2652 {
2653 struct di_devlink *duplink;
2654
2655 if (!devlink_valid(devlink)) {
2656 errno = EINVAL;
2657 return (NULL);
2658 }
2659
2660 if ((duplink = calloc(1, sizeof (struct di_devlink))) == NULL) {
2661 return (NULL);
2662 }
2663
2664 duplink->rel_path = strdup(devlink->rel_path);
2665 duplink->abs_path = strdup(devlink->abs_path);
2666 duplink->content = strdup(devlink->content);
2667 duplink->type = devlink->type;
2668
2669 if (!devlink_valid(duplink)) {
2670 (void) di_devlink_free(duplink);
2671 errno = ENOMEM;
2672 return (NULL);
2673 }
2674
2675 return (duplink);
2676 }
2677
2678 int
di_devlink_free(di_devlink_t devlink)2679 di_devlink_free(di_devlink_t devlink)
2680 {
2681 if (devlink == NULL) {
2682 errno = EINVAL;
2683 return (-1);
2684 }
2685
2686 free(devlink->rel_path);
2687 free(devlink->abs_path);
2688 free(devlink->content);
2689 free(devlink);
2690
2691 return (0);
2692 }
2693
2694 /*
2695 * Obtain path relative to dev_dir
2696 */
2697 static const char *
rel_path(struct di_devlink_handle * hdp,const char * path)2698 rel_path(struct di_devlink_handle *hdp, const char *path)
2699 {
2700 const size_t len = strlen(hdp->dev_dir);
2701
2702 if (strncmp(path, hdp->dev_dir, len) != 0)
2703 return (NULL);
2704
2705 if (path[len] == '\0')
2706 return (&path[len]);
2707
2708 if (path[len] != '/')
2709 return (NULL);
2710
2711 return (&path[len+1]);
2712 }
2713
2714 static int
recurse_dev(struct di_devlink_handle * hdp,recurse_t * rp)2715 recurse_dev(struct di_devlink_handle *hdp, recurse_t *rp)
2716 {
2717 int ret = 0;
2718
2719 (void) do_recurse(hdp->dev_dir, hdp, rp, &ret);
2720
2721 return (ret);
2722 }
2723
2724 static int
do_recurse(const char * dir,struct di_devlink_handle * hdp,recurse_t * rp,int * retp)2725 do_recurse(
2726 const char *dir,
2727 struct di_devlink_handle *hdp,
2728 recurse_t *rp,
2729 int *retp)
2730 {
2731 size_t len;
2732 const char *rel;
2733 struct stat sbuf;
2734 char cur[PATH_MAX], *cp;
2735 int i, rv = DI_WALK_CONTINUE;
2736 finddevhdl_t handle;
2737 char *d_name;
2738
2739
2740 if ((rel = rel_path(hdp, dir)) == NULL)
2741 return (DI_WALK_CONTINUE);
2742
2743 /*
2744 * Skip directories we are not interested in.
2745 */
2746 for (i = 0; i < N_SKIP_DIRS; i++) {
2747 if (strcmp(rel, skip_dirs[i]) == 0) {
2748 (void) devlink_dprintf(DBG_STEP,
2749 "do_recurse: skipping %s\n", dir);
2750 return (DI_WALK_CONTINUE);
2751 }
2752 }
2753
2754 (void) devlink_dprintf(DBG_STEP, "do_recurse: dir = %s\n", dir);
2755
2756 if (finddev_readdir(dir, &handle) != 0)
2757 return (DI_WALK_CONTINUE);
2758
2759 (void) snprintf(cur, sizeof (cur), "%s/", dir);
2760 len = strlen(cur);
2761 cp = cur + len;
2762 len = sizeof (cur) - len;
2763
2764 for (;;) {
2765 if ((d_name = (char *)finddev_next(handle)) == NULL)
2766 break;
2767
2768 if (strlcpy(cp, d_name, len) >= len)
2769 break;
2770
2771 /*
2772 * Skip files we are not interested in.
2773 */
2774 for (i = 0; i < N_SKIP_FILES; i++) {
2775
2776 rel = rel_path(hdp, cur);
2777 if (rel == NULL || strcmp(rel, skip_files[i]) == 0) {
2778 (void) devlink_dprintf(DBG_STEP,
2779 "do_recurse: skipping %s\n", cur);
2780 goto next_entry;
2781 }
2782 }
2783
2784 if (lstat(cur, &sbuf) == 0) {
2785 if (S_ISDIR(sbuf.st_mode)) {
2786 rv = do_recurse(cur, hdp, rp, retp);
2787 } else if (S_ISLNK(sbuf.st_mode)) {
2788 rv = rp->fcn(hdp, rp->data, cur);
2789 } else {
2790 (void) devlink_dprintf(DBG_STEP,
2791 "do_recurse: Skipping entry: %s\n", cur);
2792 }
2793 } else {
2794 (void) devlink_dprintf(DBG_ERR,
2795 "do_recurse: cur(%s): lstat failed: %s\n",
2796 cur, strerror(errno));
2797 }
2798
2799 next_entry:
2800 *cp = '\0';
2801
2802 if (rv != DI_WALK_CONTINUE)
2803 break;
2804 }
2805
2806 finddev_close(handle);
2807
2808 return (rv);
2809 }
2810
2811
2812 static int
check_attr(uint32_t attr)2813 check_attr(uint32_t attr)
2814 {
2815 switch (attr & A_LINK_TYPES) {
2816 case A_PRIMARY:
2817 case A_SECONDARY:
2818 return (1);
2819 default:
2820 devlink_dprintf(DBG_ERR,
2821 "check_attr: incorrect attr(%u)\n", attr);
2822 return (0);
2823 }
2824 }
2825
2826 static int
attr2type(uint32_t attr)2827 attr2type(uint32_t attr)
2828 {
2829 switch (attr & A_LINK_TYPES) {
2830 case A_PRIMARY:
2831 return (DI_PRIMARY_LINK);
2832 case A_SECONDARY:
2833 return (DI_SECONDARY_LINK);
2834 default:
2835 devlink_dprintf(DBG_ERR,
2836 "attr2type: incorrect attr(%u)\n", attr);
2837 return (0);
2838 }
2839 }
2840
2841 /* Allocate new node and link it in */
2842 static cache_node_t *
node_insert(struct di_devlink_handle * hdp,cache_node_t * pcnp,const char * path,int insert)2843 node_insert(
2844 struct di_devlink_handle *hdp,
2845 cache_node_t *pcnp,
2846 const char *path,
2847 int insert)
2848 {
2849 cache_node_t *cnp;
2850
2851 if (path == NULL) {
2852 errno = EINVAL;
2853 SET_DB_ERR(hdp);
2854 return (NULL);
2855 }
2856
2857 if ((cnp = calloc(1, sizeof (cache_node_t))) == NULL) {
2858 SET_DB_ERR(hdp);
2859 return (NULL);
2860 }
2861
2862 if ((cnp->path = strdup(path)) == NULL) {
2863 SET_DB_ERR(hdp);
2864 free(cnp);
2865 return (NULL);
2866 }
2867
2868 cnp->parent = pcnp;
2869
2870 if (pcnp == NULL) {
2871 assert(strcmp(path, "/") == 0);
2872 assert(CACHE(hdp)->root == NULL);
2873 CACHE(hdp)->root = cnp;
2874 } else if (insert == INSERT_HEAD) {
2875 cnp->sib = pcnp->child;
2876 pcnp->child = cnp;
2877 } else if (CACHE_LAST(hdp) && CACHE_LAST(hdp)->node &&
2878 CACHE_LAST(hdp)->node->parent == pcnp &&
2879 CACHE_LAST(hdp)->node->sib == NULL) {
2880
2881 CACHE_LAST(hdp)->node->sib = cnp;
2882
2883 } else {
2884 cache_node_t **pp;
2885
2886 for (pp = &pcnp->child; *pp != NULL; pp = &(*pp)->sib)
2887 ;
2888 *pp = cnp;
2889 }
2890
2891 return (cnp);
2892 }
2893
2894 /*
2895 * Allocate a new minor and link it in either at the tail or head
2896 * of the minor list depending on the value of "prev".
2897 */
2898 static cache_minor_t *
minor_insert(struct di_devlink_handle * hdp,cache_node_t * pcnp,const char * name,const char * nodetype,cache_minor_t ** prev)2899 minor_insert(
2900 struct di_devlink_handle *hdp,
2901 cache_node_t *pcnp,
2902 const char *name,
2903 const char *nodetype,
2904 cache_minor_t **prev)
2905 {
2906 cache_minor_t *cmnp;
2907
2908 if (pcnp == NULL || name == NULL) {
2909 errno = EINVAL;
2910 SET_DB_ERR(hdp);
2911 return (NULL);
2912 }
2913
2914 /*
2915 * Some pseudo drivers don't specify nodetype. Assume pseudo if
2916 * nodetype is not specified.
2917 */
2918 if (nodetype == NULL)
2919 nodetype = DDI_PSEUDO;
2920
2921 if ((cmnp = calloc(1, sizeof (cache_minor_t))) == NULL) {
2922 SET_DB_ERR(hdp);
2923 return (NULL);
2924 }
2925
2926 cmnp->name = strdup(name);
2927 cmnp->nodetype = strdup(nodetype);
2928 if (cmnp->name == NULL || cmnp->nodetype == NULL) {
2929 SET_DB_ERR(hdp);
2930 free(cmnp->name);
2931 free(cmnp->nodetype);
2932 free(cmnp);
2933 return (NULL);
2934 }
2935
2936 cmnp->node = pcnp;
2937
2938 /* Add to node's minor list */
2939 if (prev == NULL) {
2940 cmnp->sib = pcnp->minor;
2941 pcnp->minor = cmnp;
2942 } else {
2943 assert(*prev == NULL);
2944 *prev = cmnp;
2945 }
2946
2947 return (cmnp);
2948 }
2949
2950 static cache_link_t *
link_insert(struct di_devlink_handle * hdp,cache_minor_t * cmnp,const char * path,const char * content,uint32_t attr)2951 link_insert(
2952 struct di_devlink_handle *hdp,
2953 cache_minor_t *cmnp,
2954 const char *path,
2955 const char *content,
2956 uint32_t attr)
2957 {
2958 cache_link_t *clp;
2959
2960 if (path == NULL || content == NULL || !check_attr(attr)) {
2961 errno = EINVAL;
2962 SET_DB_ERR(hdp);
2963 return (NULL);
2964 }
2965
2966 if ((clp = calloc(1, sizeof (cache_link_t))) == NULL) {
2967 SET_DB_ERR(hdp);
2968 return (NULL);
2969 }
2970
2971 clp->path = strdup(path);
2972 clp->content = strdup(content);
2973 if (clp->path == NULL || clp->content == NULL) {
2974 SET_DB_ERR(hdp);
2975 link_free(&clp);
2976 return (NULL);
2977 }
2978
2979 clp->attr = attr;
2980 hash_insert(hdp, clp);
2981 clp->minor = cmnp;
2982
2983 /* Add to minor's link list */
2984 if (cmnp != NULL) {
2985 clp->sib = cmnp->link;
2986 cmnp->link = clp;
2987 } else {
2988 clp->sib = CACHE(hdp)->dngl;
2989 CACHE(hdp)->dngl = clp;
2990 }
2991
2992 return (clp);
2993 }
2994
2995 static void
hash_insert(struct di_devlink_handle * hdp,cache_link_t * clp)2996 hash_insert(struct di_devlink_handle *hdp, cache_link_t *clp)
2997 {
2998 uint_t hval;
2999
3000 hval = hashfn(hdp, clp->path);
3001 clp->hash = CACHE_HASH(hdp, hval);
3002 CACHE_HASH(hdp, hval) = clp;
3003 }
3004
3005
3006 static struct db_node *
get_node(struct di_devlink_handle * hdp,uint32_t idx)3007 get_node(struct di_devlink_handle *hdp, uint32_t idx)
3008 {
3009 return (map_seg(hdp, idx, PROT_READ, DB_NODE));
3010 }
3011
3012 static struct db_node *
set_node(struct di_devlink_handle * hdp,uint32_t idx)3013 set_node(struct di_devlink_handle *hdp, uint32_t idx)
3014 {
3015 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_NODE));
3016 }
3017
3018 static struct db_minor *
get_minor(struct di_devlink_handle * hdp,uint32_t idx)3019 get_minor(struct di_devlink_handle *hdp, uint32_t idx)
3020 {
3021 return (map_seg(hdp, idx, PROT_READ, DB_MINOR));
3022 }
3023
3024 static struct db_minor *
set_minor(struct di_devlink_handle * hdp,uint32_t idx)3025 set_minor(struct di_devlink_handle *hdp, uint32_t idx)
3026 {
3027 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_MINOR));
3028 }
3029
3030 static struct db_link *
get_link(struct di_devlink_handle * hdp,uint32_t idx)3031 get_link(struct di_devlink_handle *hdp, uint32_t idx)
3032 {
3033 return (map_seg(hdp, idx, PROT_READ, DB_LINK));
3034 }
3035
3036 static struct db_link *
set_link(struct di_devlink_handle * hdp,uint32_t idx)3037 set_link(struct di_devlink_handle *hdp, uint32_t idx)
3038 {
3039 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_LINK));
3040 }
3041
3042 static char *
get_string(struct di_devlink_handle * hdp,uint32_t idx)3043 get_string(struct di_devlink_handle *hdp, uint32_t idx)
3044 {
3045 return (map_seg(hdp, idx, PROT_READ, DB_STR));
3046 }
3047
3048 static char *
set_string(struct di_devlink_handle * hdp,uint32_t idx)3049 set_string(struct di_devlink_handle *hdp, uint32_t idx)
3050 {
3051 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_STR));
3052 }
3053
3054
3055 /*
3056 * Returns the element corresponding to idx. If the portion of file involved
3057 * is not yet mapped, does an mmap() as well. Existing mappings are not changed.
3058 */
3059 static void *
map_seg(struct di_devlink_handle * hdp,uint32_t idx,int prot,db_seg_t seg)3060 map_seg(
3061 struct di_devlink_handle *hdp,
3062 uint32_t idx,
3063 int prot,
3064 db_seg_t seg)
3065 {
3066 int s;
3067 off_t off;
3068 size_t slen;
3069 caddr_t addr;
3070
3071 if (idx == DB_NIL) {
3072 return (NULL);
3073 }
3074
3075 if (!VALID_INDEX(hdp, seg, idx)) {
3076 (void) devlink_dprintf(DBG_ERR,
3077 "map_seg: seg(%d): invalid idx(%u)\n", seg, idx);
3078 return (NULL);
3079 }
3080
3081 /*
3082 * If the seg is already mapped in, use it if the access type is
3083 * valid.
3084 */
3085 if (DB_SEG(hdp, seg) != NULL) {
3086 if (DB_SEG_PROT(hdp, seg) != prot) {
3087 (void) devlink_dprintf(DBG_ERR,
3088 "map_seg: illegal access: "
3089 "seg[%d]: idx=%u, seg_prot=%d, access=%d\n",
3090 seg, idx, DB_SEG_PROT(hdp, seg), prot);
3091 return (NULL);
3092 }
3093 return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]);
3094 }
3095
3096 /*
3097 * Segment is not mapped. Mmap() the segment.
3098 */
3099 off = seg_size(hdp, DB_HEADER);
3100 for (s = 0; s < seg; s++) {
3101 off += seg_size(hdp, s);
3102 }
3103 slen = seg_size(hdp, seg);
3104
3105 addr = mmap(0, slen, prot, MAP_SHARED, DB(hdp)->db_fd, off);
3106 if (addr == MAP_FAILED) {
3107 (void) devlink_dprintf(DBG_ERR,
3108 "map_seg: seg[%d]: mmap failed: %s\n", seg,
3109 strerror(errno));
3110 (void) devlink_dprintf(DBG_ERR,
3111 "map_seg: args: len=%lu, prot=%d, fd=%d, off=%ld\n",
3112 (ulong_t)slen, prot, DB(hdp)->db_fd, off);
3113 return (NULL);
3114 }
3115
3116 DB_SEG(hdp, seg) = addr;
3117 DB_SEG_PROT(hdp, seg) = prot;
3118
3119 (void) devlink_dprintf(DBG_STEP, "map_seg: seg[%d]: len=%lu, prot=%d, "
3120 "fd=%d, off=%ld, seg_base=%p\n", seg, (ulong_t)slen, prot,
3121 DB(hdp)->db_fd, off, (void *)addr);
3122
3123 return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]);
3124 }
3125
3126 /*
3127 * Computes the size of a segment rounded up to the nearest page boundary.
3128 */
3129 static size_t
seg_size(struct di_devlink_handle * hdp,int seg)3130 seg_size(struct di_devlink_handle *hdp, int seg)
3131 {
3132 size_t sz;
3133
3134 assert(DB_HDR(hdp)->page_sz);
3135
3136 if (seg == DB_HEADER) {
3137 sz = HDR_LEN;
3138 } else {
3139 assert(DB_NUM(hdp, seg) >= 1);
3140 sz = DB_NUM(hdp, seg) * elem_sizes[seg];
3141 }
3142
3143 sz = (sz / DB_HDR(hdp)->page_sz) + 1;
3144
3145 sz *= DB_HDR(hdp)->page_sz;
3146
3147 return (sz);
3148 }
3149
3150 static size_t
size_db(struct di_devlink_handle * hdp,long page_sz,uint32_t * count)3151 size_db(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
3152 {
3153 int i;
3154 size_t sz;
3155 cache_link_t *clp;
3156
3157 assert(page_sz > 0);
3158
3159 /* Take "NIL" element into account */
3160 for (i = 0; i < DB_TYPES; i++) {
3161 count[i] = 1;
3162 }
3163
3164 count_node(CACHE(hdp)->root, count);
3165
3166 for (clp = CACHE(hdp)->dngl; clp != NULL; clp = clp->sib) {
3167 count_link(clp, count);
3168 }
3169
3170 sz = ((HDR_LEN / page_sz) + 1) * page_sz;
3171 for (i = 0; i < DB_TYPES; i++) {
3172 assert(count[i] >= 1);
3173 sz += (((count[i] * elem_sizes[i]) / page_sz) + 1) * page_sz;
3174 (void) devlink_dprintf(DBG_INFO, "N[%u]=%u\n", i, count[i]);
3175 }
3176 (void) devlink_dprintf(DBG_INFO, "DB size=%lu\n", (ulong_t)sz);
3177
3178 return (sz);
3179 }
3180
3181
3182 static void
count_node(cache_node_t * cnp,uint32_t * count)3183 count_node(cache_node_t *cnp, uint32_t *count)
3184 {
3185 cache_minor_t *cmnp;
3186
3187 if (cnp == NULL)
3188 return;
3189
3190 count[DB_NODE]++;
3191 count_string(cnp->path, count);
3192
3193 for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) {
3194 count_minor(cmnp, count);
3195 }
3196
3197 for (cnp = cnp->child; cnp != NULL; cnp = cnp->sib) {
3198 count_node(cnp, count);
3199 }
3200
3201 }
3202
3203 static void
count_minor(cache_minor_t * cmnp,uint32_t * count)3204 count_minor(cache_minor_t *cmnp, uint32_t *count)
3205 {
3206 cache_link_t *clp;
3207
3208 if (cmnp == NULL)
3209 return;
3210
3211 count[DB_MINOR]++;
3212 count_string(cmnp->name, count);
3213 count_string(cmnp->nodetype, count);
3214
3215 for (clp = cmnp->link; clp != NULL; clp = clp->sib) {
3216 count_link(clp, count);
3217 }
3218 }
3219
3220 static void
count_link(cache_link_t * clp,uint32_t * count)3221 count_link(cache_link_t *clp, uint32_t *count)
3222 {
3223 if (clp == NULL)
3224 return;
3225
3226 count[DB_LINK]++;
3227 count_string(clp->path, count);
3228 count_string(clp->content, count);
3229 }
3230
3231
3232 static void
count_string(const char * str,uint32_t * count)3233 count_string(const char *str, uint32_t *count)
3234 {
3235 if (str == NULL) {
3236 (void) devlink_dprintf(DBG_ERR,
3237 "count_string: NULL argument\n");
3238 return;
3239 }
3240
3241 count[DB_STR] += strlen(str) + 1;
3242 }
3243
3244 static uint_t
hashfn(struct di_devlink_handle * hdp,const char * str)3245 hashfn(struct di_devlink_handle *hdp, const char *str)
3246 {
3247 const char *cp;
3248 ulong_t hval = 0;
3249
3250 if (str == NULL) {
3251 return (0);
3252 }
3253
3254 assert(CACHE(hdp)->hash_sz >= MIN_HASH_SIZE);
3255
3256 for (cp = str; *cp != '\0'; cp++) {
3257 hval += *cp;
3258 }
3259
3260 return (hval % CACHE(hdp)->hash_sz);
3261 }
3262
3263 /*
3264 * enter_db_lock()
3265 *
3266 * If the handle is IS_RDWR then we lock as writer to "update" database,
3267 * if IS_RDONLY then we lock as reader to "snapshot" database. The
3268 * implementation uses advisory file locking.
3269 *
3270 * This function returns:
3271 * == 1 success and grabbed the lock file, we can open the DB.
3272 * == 0 success but did not lock the lock file, reader must walk
3273 * the /dev directory.
3274 * == -1 failure.
3275 */
3276 static int
enter_db_lock(struct di_devlink_handle * hdp,const char * root_dir)3277 enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir)
3278 {
3279 int fd;
3280 struct flock lock;
3281 char lockfile[PATH_MAX];
3282 int rv;
3283 int writer = HDL_RDWR(hdp);
3284 static int did_sync = 0;
3285 int eintrs;
3286
3287 assert(hdp->lock_fd < 0);
3288
3289 get_db_path(hdp, DB_LOCK, lockfile, sizeof (lockfile));
3290
3291 devlink_dprintf(DBG_LCK, "enter_db_lock: %s BEGIN\n",
3292 writer ? "update" : "snapshot");
3293
3294 /* Record locks are per-process. Protect against multiple threads. */
3295 (void) mutex_lock(&update_mutex);
3296
3297 again: if ((fd = open(lockfile,
3298 (writer ? (O_RDWR|O_CREAT) : O_RDONLY), DB_LOCK_PERMS)) < 0) {
3299 /*
3300 * Typically the lock file and the database go hand in hand.
3301 * If we find that the lock file does not exist (for some
3302 * unknown reason) and we are the reader then we return
3303 * success (after triggering devfsadm to create the file and
3304 * a retry) so that we can still provide service via slow
3305 * /dev walk. If we get a failure as a writer we want the
3306 * error to manifests itself.
3307 */
3308 if ((errno == ENOENT) && !writer) {
3309 /* If reader, signal once to get files created */
3310 if (did_sync == 0) {
3311 did_sync = 1;
3312 devlink_dprintf(DBG_LCK,
3313 "enter_db_lock: %s OSYNC\n",
3314 writer ? "update" : "snapshot");
3315
3316 /* signal to get files created */
3317 (void) devlink_create(root_dir, NULL,
3318 DCA_DEVLINK_SYNC);
3319 goto again;
3320 }
3321 devlink_dprintf(DBG_LCK,
3322 "enter_db_lock: %s OPENFAILD %s: WALK\n",
3323 writer ? "update" : "snapshot", strerror(errno));
3324 (void) mutex_unlock(&update_mutex);
3325 return (0); /* success, but not locked */
3326 } else {
3327 devlink_dprintf(DBG_LCK,
3328 "enter_db_lock: %s OPENFAILD %s\n",
3329 writer ? "update" : "snapshot", strerror(errno));
3330 (void) mutex_unlock(&update_mutex);
3331 return (-1); /* failed */
3332 }
3333 }
3334
3335 lock.l_type = writer ? F_WRLCK : F_RDLCK;
3336 lock.l_whence = SEEK_SET;
3337 lock.l_start = 0;
3338 lock.l_len = 0;
3339
3340 /* Enter the lock. */
3341 for (eintrs = 0; eintrs < MAX_LOCK_RETRY; eintrs++) {
3342 rv = fcntl(fd, F_SETLKW, &lock);
3343 if ((rv != -1) || (errno != EINTR))
3344 break;
3345 }
3346
3347 if (rv != -1) {
3348 hdp->lock_fd = fd;
3349 devlink_dprintf(DBG_LCK, "enter_db_lock: %s LOCKED\n",
3350 writer ? "update" : "snapshot");
3351 return (1); /* success, locked */
3352 }
3353
3354 (void) close(fd);
3355 devlink_dprintf(DBG_ERR, "enter_db_lock: %s FAILED: %s: WALK\n",
3356 writer ? "update" : "snapshot", strerror(errno));
3357 (void) mutex_unlock(&update_mutex);
3358 return (-1);
3359 }
3360
3361 /*
3362 * Close and re-open lock file every time so that it is recreated if deleted.
3363 */
3364 static void
exit_db_lock(struct di_devlink_handle * hdp)3365 exit_db_lock(struct di_devlink_handle *hdp)
3366 {
3367 struct flock unlock;
3368 int writer = HDL_RDWR(hdp);
3369
3370 if (hdp->lock_fd < 0) {
3371 return;
3372 }
3373
3374 unlock.l_type = F_UNLCK;
3375 unlock.l_whence = SEEK_SET;
3376 unlock.l_start = 0;
3377 unlock.l_len = 0;
3378
3379 devlink_dprintf(DBG_LCK, "exit_db_lock : %s UNLOCKED\n",
3380 writer ? "update" : "snapshot");
3381 if (fcntl(hdp->lock_fd, F_SETLK, &unlock) == -1) {
3382 devlink_dprintf(DBG_ERR, "exit_db_lock : %s failed: %s\n",
3383 writer ? "update" : "snapshot", strerror(errno));
3384 }
3385
3386 (void) close(hdp->lock_fd);
3387
3388 hdp->lock_fd = -1;
3389
3390 (void) mutex_unlock(&update_mutex);
3391 }
3392
3393 /*
3394 * returns 1 if contents is a minor node in /devices.
3395 * If mn_root is not NULL, mn_root is set to:
3396 * if contents is a /dev node, mn_root = contents
3397 * OR
3398 * if contents is a /devices node, mn_root set to the '/'
3399 * following /devices.
3400 */
3401 int
is_minor_node(const char * contents,const char ** mn_root)3402 is_minor_node(const char *contents, const char **mn_root)
3403 {
3404 char *ptr, *prefix;
3405
3406 prefix = "../devices/";
3407
3408 if ((ptr = strstr(contents, prefix)) != NULL) {
3409
3410 /* mn_root should point to the / following /devices */
3411 if (mn_root != NULL) {
3412 *mn_root = ptr += strlen(prefix) - 1;
3413 }
3414 return (1);
3415 }
3416
3417 prefix = "/devices/";
3418
3419 if (strncmp(contents, prefix, strlen(prefix)) == 0) {
3420
3421 /* mn_root should point to the / following /devices/ */
3422 if (mn_root != NULL) {
3423 *mn_root = contents + strlen(prefix) - 1;
3424 }
3425 return (1);
3426 }
3427
3428 if (mn_root != NULL) {
3429 *mn_root = contents;
3430 }
3431 return (0);
3432 }
3433
3434 static int
s_readlink(const char * link,char * buf,size_t blen)3435 s_readlink(const char *link, char *buf, size_t blen)
3436 {
3437 int rv;
3438
3439 if ((rv = readlink(link, buf, blen)) == -1)
3440 goto bad;
3441
3442 if (rv >= blen && buf[blen - 1] != '\0') {
3443 errno = ENAMETOOLONG;
3444 goto bad;
3445 } else if (rv < blen) {
3446 buf[rv] = '\0';
3447 }
3448
3449 return (0);
3450 bad:
3451 devlink_dprintf(DBG_ERR, "s_readlink: %s: failed: %s\n",
3452 link, strerror(errno));
3453 return (-1);
3454 }
3455
3456 /*
3457 * Synchronous link creation interface routines
3458 * The scope of the operation is determined by the "name" arg.
3459 * "name" can be NULL, a driver name or a devfs pathname (without /devices)
3460 *
3461 * "name" creates
3462 * ====== =======
3463 *
3464 * NULL => All devlinks in system
3465 * <driver> => devlinks for named driver
3466 * /pci@1 => devlinks for subtree rooted at pci@1
3467 * /pseudo/foo@0:X => devlinks for minor X
3468 *
3469 * devlink_create() returns 0 on success or an errno value on failure
3470 */
3471
3472 #define MAX_DAEMON_ATTEMPTS 2
3473
3474 static int
devlink_create(const char * root,const char * name,int dca_devlink_flag)3475 devlink_create(const char *root, const char *name, int dca_devlink_flag)
3476 {
3477 int i;
3478 int install;
3479 struct dca_off dca;
3480
3481 assert(root);
3482
3483 /*
3484 * Convert name into arg for door_call
3485 */
3486 if (dca_init(name, &dca, dca_devlink_flag) != 0)
3487 return (EINVAL);
3488
3489 /*
3490 * Attempt to use the daemon first
3491 */
3492 i = 0;
3493 do {
3494 install = daemon_call(root, &dca);
3495
3496 devlink_dprintf(DBG_INFO, "daemon_call() retval=%d\n",
3497 dca.dca_error);
3498
3499 /*
3500 * Retry only if door server isn't running
3501 */
3502 if (dca.dca_error != ENOENT && dca.dca_error != EBADF) {
3503 return (dca.dca_error);
3504 }
3505
3506 dca.dca_error = 0;
3507
3508 /*
3509 * To improve performance defer this check until the first
3510 * failure. Safe to defer as door server checks perms.
3511 */
3512 if (geteuid() != 0)
3513 return (EPERM);
3514 /*
3515 * Daemon may not be running. Try to start it.
3516 */
3517 } while ((++i < MAX_DAEMON_ATTEMPTS) &&
3518 start_daemon(root, install) == 0);
3519
3520 devlink_dprintf(DBG_INFO, "devlink_create: can't start daemon\n");
3521
3522 assert(dca.dca_error == 0);
3523
3524 /*
3525 * If the daemon cannot be started execute the devfsadm command.
3526 */
3527 exec_cmd(root, &dca);
3528
3529 return (dca.dca_error);
3530 }
3531
3532 /*
3533 * The "name" member of "struct dca" contains data in the following order
3534 * root'\0'minor'\0'driver'\0'
3535 * The root component is always present at offset 0 in the "name" field.
3536 * The driver and minor are optional. If present they have a non-zero
3537 * offset in the "name" member.
3538 */
3539 static int
dca_init(const char * name,struct dca_off * dcp,int dca_flags)3540 dca_init(const char *name, struct dca_off *dcp, int dca_flags)
3541 {
3542 char *cp;
3543
3544 dcp->dca_root = 0;
3545 dcp->dca_minor = 0;
3546 dcp->dca_driver = 0;
3547 dcp->dca_error = 0;
3548 dcp->dca_flags = dca_flags;
3549 dcp->dca_name[0] = '\0';
3550
3551 name = name ? name : "/";
3552
3553 /*
3554 * Check if name is a driver name
3555 */
3556 if (*name != '/') {
3557 (void) snprintf(dcp->dca_name, sizeof (dcp->dca_name),
3558 "/ %s", name);
3559 dcp->dca_root = 0;
3560 *(dcp->dca_name + 1) = '\0';
3561 dcp->dca_driver = 2;
3562 return (0);
3563 }
3564
3565 (void) snprintf(dcp->dca_name, sizeof (dcp->dca_name), "%s", name);
3566
3567 /*
3568 * "/devices" not allowed in devfs pathname
3569 */
3570 if (is_minor_node(name, NULL))
3571 return (-1);
3572
3573 dcp->dca_root = 0;
3574 if ((cp = strrchr(dcp->dca_name, ':')) != NULL) {
3575 *cp++ = '\0';
3576 dcp->dca_minor = cp - dcp->dca_name;
3577 }
3578
3579 return (0);
3580 }
3581
3582
3583 #define DAEMON_STARTUP_TIME 1 /* 1 second. This may need to be adjusted */
3584 #define DEVNAME_CHECK_FILE "/etc/devname_check_RDONLY"
3585
3586 static int
daemon_call(const char * root,struct dca_off * dcp)3587 daemon_call(const char *root, struct dca_off *dcp)
3588 {
3589 door_arg_t arg;
3590 int fd, door_error = 0;
3591 sigset_t oset, nset;
3592 char synch_door[PATH_MAX];
3593 struct stat sb;
3594 char *prefix;
3595 int rofd;
3596 int rdonly;
3597 int install = 0;
3598
3599 /*
3600 * If root is readonly, there are two possibilities:
3601 * - we are in some sort of install scenario
3602 * - we are early in boot
3603 * If the latter we don't want daemon_call() to succeed.
3604 * else we want to use /tmp/etc/dev
3605 *
3606 * Both of these requrements are fulfilled if we check for
3607 * for a root owned door file in /tmp/etc/dev. If we are
3608 * early in boot, the door file won't exist, so this call
3609 * will fail.
3610 *
3611 * If we are in install, the door file will be present.
3612 *
3613 * If root is read-only, try only once, since libdevinfo
3614 * isn't capable of starting devfsadmd correctly in that
3615 * situation.
3616 *
3617 * Don't use statvfs() to check for readonly roots since it
3618 * doesn't always report the truth.
3619 */
3620 rofd = -1;
3621 rdonly = 0;
3622 if ((rofd = open(DEVNAME_CHECK_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644))
3623 == -1 && errno == EROFS) {
3624 rdonly = 1;
3625 prefix = "/tmp";
3626 } else {
3627 if (rofd != -1) {
3628 (void) close(rofd);
3629 (void) unlink(DEVNAME_CHECK_FILE);
3630 }
3631 prefix = (char *)root;
3632 }
3633
3634 if (rdonly && stat(DEVNAME_CHECK_FILE, &sb) != -1)
3635 install = 1;
3636
3637 (void) snprintf(synch_door, sizeof (synch_door),
3638 "%s/etc/dev/%s", prefix, DEVFSADM_SYNCH_DOOR);
3639
3640 /*
3641 * Return ENOTSUP to prevent retries if root is readonly
3642 */
3643 if (stat(synch_door, &sb) == -1 || sb.st_uid != 0) {
3644 if (rdonly)
3645 dcp->dca_error = ENOTSUP;
3646 else
3647 dcp->dca_error = ENOENT;
3648 devlink_dprintf(DBG_ERR,
3649 "stat failed: %s: no file or not root owned\n", synch_door);
3650 return (install);
3651 }
3652
3653 if ((fd = open(synch_door, O_RDONLY)) == -1) {
3654 dcp->dca_error = errno;
3655 devlink_dprintf(DBG_ERR, "open of %s failed: %s\n",
3656 synch_door, strerror(errno));
3657 return (install);
3658 }
3659
3660 arg.data_ptr = (char *)dcp;
3661 arg.data_size = sizeof (*dcp);
3662 arg.desc_ptr = NULL;
3663 arg.desc_num = 0;
3664 arg.rbuf = (char *)dcp;
3665 arg.rsize = sizeof (*dcp);
3666
3667 /*
3668 * Block signals to this thread until door call
3669 * completes.
3670 */
3671 (void) sigfillset(&nset);
3672 (void) sigemptyset(&oset);
3673 (void) sigprocmask(SIG_SETMASK, &nset, &oset);
3674 if (door_call(fd, &arg)) {
3675 door_error = 1;
3676 dcp->dca_error = errno;
3677 }
3678 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
3679
3680 (void) close(fd);
3681
3682 if (door_error)
3683 return (install);
3684
3685 assert(arg.data_ptr);
3686
3687 /*LINTED*/
3688 dcp->dca_error = ((struct dca_off *)arg.data_ptr)->dca_error;
3689
3690 /*
3691 * The doors interface may return data in a different buffer
3692 * If that happens, deallocate buffer via munmap()
3693 */
3694 if (arg.rbuf != (char *)dcp)
3695 (void) munmap(arg.rbuf, arg.rsize);
3696
3697 return (install);
3698 }
3699
3700 #define DEVFSADM_PATH "/usr/sbin/devfsadm"
3701 #define DEVFSADM "devfsadm"
3702
3703 #define DEVFSADMD_PATH "/usr/lib/devfsadm/devfsadmd"
3704 #define DEVFSADM_DAEMON "devfsadmd"
3705
3706 static int
start_daemon(const char * root,int install)3707 start_daemon(const char *root, int install)
3708 {
3709 int rv, i = 0;
3710 char *argv[20];
3711
3712 argv[i++] = DEVFSADM_DAEMON;
3713 if (install) {
3714 argv[i++] = "-a";
3715 argv[i++] = "/tmp";
3716 argv[i++] = "-p";
3717 argv[i++] = "/tmp/root/etc/path_to_inst";
3718 } else if (strcmp(root, "/")) {
3719 argv[i++] = "-r";
3720 argv[i++] = (char *)root;
3721 }
3722 argv[i++] = NULL;
3723
3724 rv = do_exec(DEVFSADMD_PATH, argv);
3725
3726 (void) sleep(DAEMON_STARTUP_TIME);
3727
3728 return (rv);
3729 }
3730
3731 static void
exec_cmd(const char * root,struct dca_off * dcp)3732 exec_cmd(const char *root, struct dca_off *dcp)
3733 {
3734 int i;
3735 char *argv[20];
3736
3737 i = 0;
3738 argv[i++] = DEVFSADM;
3739
3740 /*
3741 * Load drivers only if -i is specified
3742 */
3743 if (dcp->dca_driver) {
3744 argv[i++] = "-i";
3745 argv[i++] = &dcp->dca_name[dcp->dca_driver];
3746 } else {
3747 argv[i++] = "-n";
3748 }
3749
3750 if (root != NULL && strcmp(root, "/") != 0) {
3751 argv[i++] = "-r";
3752 argv[i++] = (char *)root;
3753 }
3754
3755 argv[i] = NULL;
3756
3757 if (do_exec(DEVFSADM_PATH, argv))
3758 dcp->dca_error = errno;
3759 }
3760
3761 static int
do_exec(const char * path,char * const argv[])3762 do_exec(const char *path, char *const argv[])
3763 {
3764 int i;
3765 pid_t cpid;
3766
3767 #ifdef DEBUG
3768 devlink_dprintf(DBG_INFO, "Executing %s\n\tArgument list:", path);
3769 for (i = 0; argv[i] != NULL; i++) {
3770 devlink_dprintf(DBG_INFO, " %s", argv[i]);
3771 }
3772 devlink_dprintf(DBG_INFO, "\n");
3773 #endif
3774
3775 if ((cpid = fork1()) == -1) {
3776 devlink_dprintf(DBG_ERR, "fork1 failed: %s\n", strerror(errno));
3777 return (-1);
3778 }
3779
3780 if (cpid == 0) { /* child process */
3781 int fd;
3782
3783 if ((fd = open("/dev/null", O_RDWR)) >= 0) {
3784 (void) dup2(fd, fileno(stdout));
3785 (void) dup2(fd, fileno(stderr));
3786 (void) close(fd);
3787
3788 (void) execv(path, argv);
3789 } else {
3790 devlink_dprintf(DBG_ERR,
3791 "open of /dev/null failed: %s\n", strerror(errno));
3792 }
3793
3794 _exit(-1);
3795 }
3796
3797 /* Parent process */
3798 if (waitpid(cpid, &i, 0) == cpid) {
3799 if (WIFEXITED(i)) {
3800 if (WEXITSTATUS(i) == 0) {
3801 devlink_dprintf(DBG_STEP,
3802 "do_exec: child exited normally\n");
3803 return (0);
3804 } else
3805 errno = EINVAL;
3806 } else {
3807 /*
3808 * The child was interrupted by a signal
3809 */
3810 errno = EINTR;
3811 }
3812 devlink_dprintf(DBG_ERR, "child terminated abnormally: %s\n",
3813 strerror(errno));
3814 } else {
3815 devlink_dprintf(DBG_ERR, "waitpid failed: %s\n",
3816 strerror(errno));
3817 }
3818
3819 return (-1);
3820 }
3821
3822 static int
walk_cache_links(di_devlink_handle_t hdp,cache_link_t * clp,link_desc_t * linkp)3823 walk_cache_links(di_devlink_handle_t hdp, cache_link_t *clp, link_desc_t *linkp)
3824 {
3825 int i;
3826
3827 assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp));
3828
3829 devlink_dprintf(DBG_INFO, "walk_cache_links: initial link: %s\n",
3830 clp ? clp->path : "<NULL>");
3831
3832 /*
3833 * First search the links under the specified minor. On the
3834 * 2nd pass, search the dangling list - secondary links may
3835 * exist on this list since they are not resolved during the
3836 * /dev walk.
3837 */
3838 for (i = 0; i < 2; i++) {
3839 for (; clp != NULL; clp = clp->sib) {
3840 struct di_devlink vlink = {NULL};
3841
3842 assert(clp->path[0] != '/');
3843
3844 vlink.rel_path = clp->path;
3845 vlink.content = clp->content;
3846 vlink.type = attr2type(clp->attr);
3847
3848 if (visit_link(hdp, linkp, &vlink)
3849 != DI_WALK_CONTINUE) {
3850 devlink_dprintf(DBG_INFO, "walk_cache_links: "
3851 "terminating at link: %s\n", clp->path);
3852 goto out;
3853 }
3854 }
3855
3856 clp = CACHE(hdp)->dngl;
3857 }
3858
3859 out:
3860
3861 /* If i < 2, we terminated the walk prematurely */
3862 return (i < 2 ? DI_WALK_TERMINATE : DI_WALK_CONTINUE);
3863 }
3864
3865 static void
walk_all_cache(di_devlink_handle_t hdp,link_desc_t * linkp)3866 walk_all_cache(di_devlink_handle_t hdp, link_desc_t *linkp)
3867 {
3868 int i;
3869 cache_link_t *clp;
3870
3871 devlink_dprintf(DBG_INFO, "walk_all_cache: entered\n");
3872
3873 for (i = 0; i < CACHE(hdp)->hash_sz; i++) {
3874 clp = CACHE_HASH(hdp, i);
3875 for (; clp; clp = clp->hash) {
3876 struct di_devlink vlink = {NULL};
3877
3878 assert(clp->path[0] != '/');
3879
3880 vlink.rel_path = clp->path;
3881 vlink.content = clp->content;
3882 vlink.type = attr2type(clp->attr);
3883 if (visit_link(hdp, linkp, &vlink) !=
3884 DI_WALK_CONTINUE) {
3885 devlink_dprintf(DBG_INFO, "walk_all_cache: "
3886 "terminating walk at link: %s\n",
3887 clp->path);
3888 return;
3889 }
3890 }
3891 }
3892 }
3893
3894 static void
walk_cache_minor(di_devlink_handle_t hdp,const char * mpath,link_desc_t * linkp)3895 walk_cache_minor(di_devlink_handle_t hdp, const char *mpath, link_desc_t *linkp)
3896 {
3897 cache_minor_t *cmnp;
3898
3899 assert(mpath);
3900
3901 if ((cmnp = lookup_minor(hdp, mpath, NULL, TYPE_CACHE)) != NULL) {
3902 (void) walk_cache_links(hdp, cmnp->link, linkp);
3903 } else {
3904 devlink_dprintf(DBG_ERR, "lookup minor failed: %s\n", mpath);
3905 }
3906 }
3907
3908 static void
walk_cache_node(di_devlink_handle_t hdp,const char * path,link_desc_t * linkp)3909 walk_cache_node(di_devlink_handle_t hdp, const char *path, link_desc_t *linkp)
3910 {
3911 cache_minor_t *cmnp;
3912 cache_node_t *cnp;
3913
3914 assert(path);
3915
3916 if ((cnp = lookup_node(hdp, (char *)path, TYPE_CACHE)) == NULL) {
3917 devlink_dprintf(DBG_ERR, "lookup node failed: %s\n", path);
3918 return;
3919 }
3920
3921 for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) {
3922 if (walk_cache_links(hdp, cmnp->link, linkp)
3923 == DI_WALK_TERMINATE)
3924 break;
3925 }
3926 }
3927
3928 /*
3929 * Private function
3930 *
3931 * Walk cached links corresponding to the given path.
3932 *
3933 * path path to a node or minor node.
3934 *
3935 * flags specifies the type of devlinks to be selected.
3936 * If DI_PRIMARY_LINK is used, only primary links are selected.
3937 * If DI_SECONDARY_LINK is specified, only secondary links
3938 * are selected.
3939 * If neither flag is specified, all devlinks are selected.
3940 *
3941 * re An extended regular expression in regex(7) format which
3942 * selects the /dev links to be returned. The regular
3943 * expression should use link pathnames relative to
3944 * /dev. i.e. without the leading "/dev/" prefix.
3945 * A NULL value matches all devlinks.
3946 */
3947 int
di_devlink_cache_walk(di_devlink_handle_t hdp,const char * re,const char * path,uint_t flags,void * arg,int (* devlink_callback)(di_devlink_t,void *))3948 di_devlink_cache_walk(di_devlink_handle_t hdp,
3949 const char *re,
3950 const char *path,
3951 uint_t flags,
3952 void *arg,
3953 int (*devlink_callback)(di_devlink_t, void *))
3954 {
3955 regex_t reg;
3956 link_desc_t linkd = {NULL};
3957
3958 if (hdp == NULL || path == NULL || !link_flag(flags) ||
3959 !HDL_RDWR(hdp) || devlink_callback == NULL) {
3960 errno = EINVAL;
3961 return (-1);
3962 }
3963
3964 linkd.flags = flags;
3965 linkd.arg = arg;
3966 linkd.fcn = devlink_callback;
3967
3968 if (re) {
3969 if (regcomp(®, re, REG_EXTENDED) != 0)
3970 return (-1);
3971 linkd.regp = ®
3972 }
3973
3974 if (minor_colon(path) == NULL) {
3975 walk_cache_node(hdp, path, &linkd);
3976 } else {
3977 walk_cache_minor(hdp, path, &linkd);
3978 }
3979
3980 if (re)
3981 regfree(®);
3982
3983 return (0);
3984 }
3985
3986 #define DEBUG_ENV_VAR "_DEVLINK_DEBUG"
3987 static int _devlink_debug = -1;
3988
3989 /*
3990 * debug level is initialized to -1.
3991 * On first call into this routine, debug level is set.
3992 * If debug level is zero, debugging msgs are disabled.
3993 */
3994 static void
debug_print(debug_level_t msglevel,const char * fmt,va_list ap)3995 debug_print(debug_level_t msglevel, const char *fmt, va_list ap)
3996 {
3997 char *cp;
3998 int save;
3999
4000 /*
4001 * We shouldn't be here if debug is disabled
4002 */
4003 assert(_devlink_debug != 0);
4004
4005 /*
4006 * Set debug level on first call into this routine
4007 */
4008 if (_devlink_debug < 0) {
4009 if ((cp = getenv(DEBUG_ENV_VAR)) == NULL) {
4010 _devlink_debug = 0;
4011 return;
4012 }
4013
4014 save = errno;
4015 errno = 0;
4016 _devlink_debug = strtol(cp, NULL, 10);
4017 if (errno != 0 || _devlink_debug < 0) {
4018 _devlink_debug = 0;
4019 errno = save;
4020 return;
4021 }
4022 errno = save;
4023
4024 if (!_devlink_debug)
4025 return;
4026 }
4027
4028 /* debug msgs are enabled */
4029 assert(_devlink_debug > 0);
4030
4031 if (_devlink_debug < msglevel)
4032 return;
4033 if ((_devlink_debug == DBG_LCK) && (msglevel != _devlink_debug))
4034 return;
4035
4036 /* Print a distinctive label for error msgs */
4037 if (msglevel == DBG_ERR) {
4038 (void) fprintf(stderr, "[ERROR]: ");
4039 }
4040
4041 (void) vfprintf(stderr, fmt, ap);
4042 (void) fflush(stderr);
4043 }
4044
4045 /* ARGSUSED */
4046 /* PRINTFLIKE2 */
4047 void
devlink_dprintf(debug_level_t msglevel,const char * fmt,...)4048 devlink_dprintf(debug_level_t msglevel, const char *fmt, ...)
4049 {
4050 va_list ap;
4051
4052 assert(msglevel > 0);
4053 if (!_devlink_debug)
4054 return;
4055
4056 va_start(ap, fmt);
4057 debug_print(msglevel, fmt, ap);
4058 va_end(ap);
4059 }
4060