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