1 /* $NetBSD: dev_mkdb.c,v 1.32 2026/01/02 16:31:00 nia Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: dev_mkdb.c,v 1.32 2026/01/02 16:31:00 nia Exp $"); 34 35 #include <sys/queue.h> 36 #include <sys/stat.h> 37 38 #include <cdbw.h> 39 #include <db.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <endian.h> 43 #include <fcntl.h> 44 #include <fts.h> 45 #include <paths.h> 46 #include <search.h> 47 #include <stdint.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <util.h> 53 54 #define HASH_SIZE 65536 55 #define FILE_PERMISSION S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH 56 57 static struct cdbw *db; 58 static DB *db_compat; 59 static const char *db_name; 60 static char *db_name_tmp; 61 62 static void usage(void) __dead; 63 64 static void 65 cdb_open(void) 66 { 67 db = cdbw_open(); 68 if (db == NULL) 69 err(1, "opening cdb writer failed"); 70 } 71 72 static void 73 cdb_close(void) 74 { 75 int fd; 76 77 fd = open(db_name_tmp, O_CREAT|O_EXCL|O_WRONLY, FILE_PERMISSION); 78 if (fd == -1) 79 err(1, "opening %s failed", db_name_tmp); 80 if (cdbw_output(db, fd, "NetBSD6 devdb", NULL)) 81 err(1, "failed to write temporary database %s", db_name_tmp); 82 cdbw_close(db); 83 db = NULL; 84 if (close(fd)) 85 err(1, "failed to write temporary database %s", db_name_tmp); 86 } 87 88 static void 89 cdb_add_entry(dev_t dev, mode_t type, const char *relpath) 90 { 91 uint8_t *buf; 92 size_t len; 93 94 len = strlen(relpath) + 1; 95 buf = malloc(len + 10); 96 le64enc(buf, dev); 97 le16enc(buf + 8, type); 98 memcpy(buf + 10, relpath, len); 99 cdbw_put(db, buf, 10, buf, len + 10); 100 free(buf); 101 } 102 103 static void 104 compat_open(void) 105 { 106 static HASHINFO openinfo = { 107 4096, /* bsize */ 108 128, /* ffactor */ 109 1024, /* nelem */ 110 2048 * 1024, /* cachesize */ 111 NULL, /* hash() */ 112 0 /* lorder */ 113 }; 114 115 db_compat = dbopen(db_name_tmp, O_CREAT|O_EXCL|O_EXLOCK|O_RDWR|O_TRUNC, 116 FILE_PERMISSION, DB_HASH, &openinfo); 117 118 if (db_compat == NULL) 119 err(1, "failed to create temporary database %s", 120 db_name_tmp); 121 } 122 123 static void 124 compat_close(void) 125 { 126 if ((*db_compat->close)(db_compat)) 127 err(1, "failed to write temporary database %s", db_name_tmp); 128 } 129 130 static void 131 compat_add_entry(dev_t dev, mode_t type, const char *relpath) 132 { 133 /* 134 * Keys are a mode_t followed by a dev_t. The former is the type of 135 * the file (mode & S_IFMT), the latter is the st_rdev field. Note 136 * that the structure may contain padding, so we have to clear it 137 * out here. 138 */ 139 struct { 140 mode_t type; 141 dev_t dev; 142 } bkey; 143 struct { 144 mode_t type; 145 int32_t dev; 146 } obkey; 147 DBT data, key; 148 149 (void)memset(&bkey, 0, sizeof(bkey)); 150 key.data = &bkey; 151 key.size = sizeof(bkey); 152 data.data = __UNCONST(relpath); 153 data.size = strlen(relpath) + 1; 154 bkey.type = type; 155 bkey.dev = dev; 156 if ((*db_compat->put)(db_compat, &key, &data, 0)) 157 err(1, "failed to write temporary database %s", db_name_tmp); 158 159 /* 160 * If the device fits into the old 32bit format, add compat entry 161 * for pre-NetBSD6 libc. 162 */ 163 164 if ((dev_t)(int32_t)dev != dev) 165 return; 166 167 (void)memset(&obkey, 0, sizeof(obkey)); 168 key.data = &obkey; 169 key.size = sizeof(obkey); 170 data.data = __UNCONST(relpath); 171 data.size = strlen(relpath) + 1; 172 obkey.type = type; 173 obkey.dev = (int32_t)dev; 174 if ((*db_compat->put)(db_compat, &key, &data, 0)) 175 err(1, "failed to write temporary database %s", db_name_tmp); 176 } 177 178 int 179 main(int argc, char **argv) 180 { 181 struct stat *st; 182 FTS *ftsp; 183 FTSENT *p; 184 int ch; 185 char *pathv[2]; 186 size_t dlen; 187 int compat_mode; 188 189 setprogname(argv[0]); 190 compat_mode = 0; 191 192 while ((ch = getopt(argc, argv, "co:")) != -1) 193 switch (ch) { 194 case 'c': 195 compat_mode = 1; 196 break; 197 case 'o': 198 db_name = optarg; 199 break; 200 default: 201 usage(); 202 } 203 argc -= optind; 204 argv += optind; 205 206 if (argc > 1) 207 usage(); 208 209 pathv[1] = NULL; 210 if (argc == 1) 211 pathv[0] = argv[0]; 212 else 213 pathv[0] = __UNCONST(_PATH_DEV); 214 215 ftsp = fts_open(pathv, FTS_NOCHDIR | FTS_PHYSICAL, NULL); 216 if (ftsp == NULL) 217 err(1, "fts_open: %s", pathv[0]); 218 219 if (db_name == NULL) { 220 if (compat_mode) 221 db_name = _PATH_DEVDB; 222 else 223 db_name = _PATH_DEVCDB; 224 } 225 easprintf(&db_name_tmp, "%s.XXXXXXX", db_name); 226 mktemp(db_name_tmp); 227 228 if (compat_mode) 229 compat_open(); 230 else 231 cdb_open(); 232 233 while ((p = fts_read(ftsp)) != NULL) { 234 if (p->fts_info != FTS_DEFAULT) 235 continue; 236 237 st = p->fts_statp; 238 if (!S_ISCHR(st->st_mode) && !S_ISBLK(st->st_mode)) 239 continue; 240 dlen = strlen(pathv[0]); 241 while (pathv[0][dlen] == '/') 242 ++dlen; 243 if (compat_mode) 244 compat_add_entry(st->st_rdev, st->st_mode & S_IFMT, 245 p->fts_path + dlen); 246 else 247 cdb_add_entry(st->st_rdev, st->st_mode & S_IFMT, 248 p->fts_path + dlen); 249 } 250 (void)fts_close(ftsp); 251 252 if (compat_mode) 253 compat_close(); 254 else 255 cdb_close(); 256 257 if (rename(db_name_tmp, db_name) == -1) 258 err(1, "rename %s to %s", db_name_tmp, db_name); 259 return 0; 260 } 261 262 static void 263 usage(void) 264 { 265 266 (void)fprintf(stderr, "Usage: %s [-c] [-o database] [directory]\n", 267 getprogname()); 268 exit(1); 269 } 270