1 /* $NetBSD: nbdb_util.c,v 1.2 2026/05/09 18:49:16 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* nbdb_util 3 6 /* SUMMARY 7 /* Non-Berkeley-DB migration support 8 /* SYNOPSIS 9 /* #include <nbdb_util.h> 10 /* 11 /* int nbdb_level; 12 /* bool nbdb_reindexing_lockout; 13 /* 14 /* void nbdb_util_init(const char *level_name) 15 /* 16 /* int nbdb_is_legacy_type(const char *type) 17 /* 18 /* const char *nbdb_find_non_leg_suffix(const char *non_leg_type) 19 /* 20 /* const char *nbdb_map_leg_type( 21 /* const char *leg_type, 22 /* const char *name, 23 /* const DICT_OPEN_INFO **open_info, 24 /* VSTRING *why) 25 /* DESCRIPTION 26 /* This module contains utilities that may be called by clients 27 /* and servers of the non-Berkeley-DB migration service. 28 /* 29 /* nbdb_is_legacy_type() returns true if the specified database 30 /* type is a Berkeley DB type ('hash' or 'btree'). 31 /* 32 /* nbdb_find_non_leg_suffix() returns the database pathname suffix 33 /* for the specified non-legacy database, or null if no suffix 34 /* was found. 35 /* 36 /* nbdb_map_leg_type() implements the mapping from expected legacy 37 /* type names to expected non-legacy type names. This supports 38 /* a user-defined mapping override, indexed by legacy type or legacy 39 /* type:name. nbdb_map_leg_type() returns null in case of error: 40 /* a table lookup error, an unexpected input type, or an unexpected 41 /* output type. 42 /* .IP leg_type 43 /* A legacy database type to be mapped to a non-legacy type. 44 /* .IP name 45 /* A database name. 46 /* .IP open_info 47 /* Null pointer, or pointer to DICT_OPEN_INFO pointer with function 48 /* pointers to open the non-legacy database. 49 /* .IP why 50 /* Storage for descriptive text in case of error. 51 /* .PP 52 /* 53 /* nbdb_util_init() converts the non-Berkeley-DB migration level 54 /* from text to internal form, and updates the nbdb_level global 55 /* variable: 56 /* .IP "redirect (internal form: NBDB_LEV_CODE_REDIRECT)" 57 /* Enables the mapping from legacy Berkeley DB types to preferred 58 /* types. 59 /* .IP "reindex (internal form: NBDB_LEV_CODE_REINDEX)" 60 /* As "redirect", but also makes requests to a reindexing service 61 /* if the non-legacy indexed file does not exist. 62 /* Exception: calling a reindexing service will remain disabled 63 /* if the process sets nbdb_reindexing_lockout to 'true'. 64 /* SEE ALSO 65 /* nbdb_reindexd(8), helper daemon to reindex legacy tables 66 /* LICENSE 67 /* .ad 68 /* .fi 69 /* The Secure Mailer license must be distributed with this software. 70 /* AUTHOR(S) 71 /* Wietse Venema 72 /* porcupine.org 73 /*--*/ 74 75 /* 76 * System library. 77 */ 78 #include <sys_defs.h> 79 #include <string.h> 80 #include <errno.h> 81 82 /* 83 * Utility library. 84 */ 85 #include <dict_cdb.h> 86 #include <dict_db.h> 87 #include <dict.h> 88 #include <dict_lmdb.h> 89 #include <mkmap.h> 90 #include <msg.h> 91 #include <mymalloc.h> 92 #include <name_code.h> 93 #include <split_at.h> 94 #include <stringops.h> 95 #include <vstring.h> 96 #include <stringops.h> 97 98 /* 99 * Global library. 100 */ 101 #include <mail_params.h> 102 #include <nbdb_util.h> 103 #include <nbdb_redirect.h> 104 #include <nbdb_surrogate.h> 105 106 /* 107 * Application-specific. 108 */ 109 int nbdb_level; 110 bool nbdb_reindexing_lockout; 111 112 /* 113 * Mapping from legacy type to non-legacy. 114 */ 115 static DICT *nbdb_cust_map; 116 117 /* 118 * Mapping from non-legacy type to pathname suffix. This should be provided 119 * by individual table implementations, but that would require changes to 120 * the dict/mkmap plugin API. 121 */ 122 typedef struct { 123 const char *type; 124 const char *suffix; 125 } NBDB_PATH_SUFFIX; 126 127 static const NBDB_PATH_SUFFIX nbdb_path_suffixes[] = { 128 DICT_TYPE_CDB, ".cdb", 129 DICT_TYPE_LMDB, ".lmdb", 130 0, 0, /* Default case */ 131 }; 132 133 /* 134 * SLMs. 135 */ 136 #define STR(x) vstring_str(x) 137 138 /* nbdb_map_leg_type - map legacy type to non-legacy type, null means error */ 139 140 const char *nbdb_map_leg_type(const char *leg_type, const char *name, 141 const DICT_OPEN_INFO **open_info, 142 VSTRING *why) 143 { 144 static VSTRING *type_name = 0; 145 const char *non_leg_type = 0; 146 const DICT_OPEN_INFO *my_open_info; 147 148 if (type_name == 0) 149 type_name = vstring_alloc(100); 150 151 /* 152 * Sanity check. 153 */ 154 if (!nbdb_is_legacy_type(leg_type)) { 155 vstring_sprintf(why, "request to map from non-Berkeley-DB type: '%s'", 156 leg_type); 157 return (0); 158 } 159 /* Search for custom mapping with the type:name. */ 160 if (nbdb_cust_map != 0) { 161 vstring_sprintf(type_name, "%s:%s", leg_type, name); 162 if ((non_leg_type = dict_get(nbdb_cust_map, STR(type_name))) == 0 163 && nbdb_cust_map->error == 0) 164 /* Search with the 'bare' type only. */ 165 non_leg_type = dict_get(nbdb_cust_map, leg_type); 166 if (non_leg_type == 0 && nbdb_cust_map->error != 0) { 167 vstring_sprintf(why, "%s:%s lookup error for '%s'", 168 nbdb_cust_map->type, nbdb_cust_map->name, 169 STR(type_name)); 170 return (0); 171 } 172 } 173 174 /* 175 * No custom mapping: use the default. 176 */ 177 if (non_leg_type == 0) { 178 if (strcmp(leg_type, DICT_TYPE_HASH) == 0) { 179 non_leg_type = (var_db_type); 180 } else if (strcmp(leg_type, DICT_TYPE_BTREE) == 0) { 181 non_leg_type = (var_cache_db_type); 182 } else { 183 msg_panic("%s: no default mapping for Berkeley DB type: '%s'", 184 __func__, leg_type); 185 } 186 } 187 188 /* 189 * More sanity checks. 190 */ 191 if (!allalnum(non_leg_type)) { 192 vstring_sprintf(why, "bad non-legacy database type syntax: '%s'", 193 non_leg_type); 194 return (0); 195 } 196 if (nbdb_is_legacy_type(non_leg_type)) { 197 vstring_sprintf(why, "mapping from '%s' to legacy type '%s'", 198 leg_type, non_leg_type); 199 return (0); 200 } 201 if ((my_open_info = dict_open_lookup(non_leg_type)) == 0) { 202 vstring_sprintf(why, "mapping to unsupported database type: '%s'", 203 non_leg_type); 204 return (0); 205 } 206 if (open_info != 0) 207 *open_info = my_open_info; 208 return (non_leg_type); 209 } 210 211 /* nbdb_is_legacy_type - sanity check */ 212 213 int nbdb_is_legacy_type(const char *leg_type) 214 { 215 return (strcmp(leg_type, DICT_TYPE_HASH) == 0 216 || strcmp(leg_type, DICT_TYPE_BTREE) == 0); 217 } 218 219 /* nbdb_find_non_leg_suffix - find file suffix for non-legacy type */ 220 221 const char *nbdb_find_non_leg_suffix(const char *non_leg_type) 222 { 223 const NBDB_PATH_SUFFIX *sp; 224 225 for (sp = nbdb_path_suffixes; sp->type != 0; sp++) 226 if (strcmp(non_leg_type, sp->type) == 0) 227 break; 228 return (sp->suffix); 229 } 230 231 /* nbdb_util_init - redirect legacy open() requests to nbdb handlers */ 232 233 void nbdb_util_init(const char *level_name) 234 { 235 static NAME_CODE nbdb_level_table[] = { 236 NBDB_LEV_NAME_NONE, NBDB_LEV_CODE_NONE, 237 NBDB_LEV_NAME_REDIRECT, NBDB_LEV_CODE_REDIRECT, 238 NBDB_LEV_NAME_REINDEX, NBDB_LEV_CODE_REINDEX, 239 0, -1, 240 }; 241 242 /* 243 * Convert the non-Berkeley-DB migration level to internal form. If 244 * migration support is disabled, then do not tun the code below. 245 */ 246 nbdb_level = name_code(nbdb_level_table, NAME_CODE_FLAG_NONE, level_name); 247 if (nbdb_level == NBDB_LEV_CODE_ERROR) 248 msg_fatal("invalid non-Berkeley-DB migration level: '%s'", level_name); 249 if (nbdb_level == NBDB_LEV_CODE_NONE) { 250 #ifdef NO_DB 251 nbdb_surr_init(); 252 #endif 253 return; 254 } 255 256 /* 257 * Initialize the custom legacy to non-legacy mapping. Discard an 258 * existing mapping, to simplify testing. To avoid a dependency loop, 259 * this mapping should never use a legacy database type. 260 */ 261 if (nbdb_cust_map) { 262 dict_close(nbdb_cust_map); 263 nbdb_cust_map = 0; 264 } 265 if (*var_nbdb_cust_map) { 266 char *map_type = mystrdup(var_nbdb_cust_map); 267 char *map_name = split_at(map_type, ':'); 268 269 if (map_name != 0 && (strcmp(map_type, DICT_TYPE_HASH) == 0 270 || strcmp(map_type, DICT_TYPE_BTREE) == 0)) { 271 nbdb_cust_map = dict_surrogate(map_type, map_name, 272 O_RDONLY, DICT_FLAG_LOCK, 273 "%s must not use legacy type: '%s'", 274 VAR_NBDB_CUST_MAP, 275 map_type); 276 } else { 277 nbdb_cust_map = dict_open(var_nbdb_cust_map, O_RDONLY, 278 DICT_FLAG_LOCK 279 | DICT_FLAG_FOLD_FIX 280 | DICT_FLAG_UTF8_REQUEST); 281 } 282 myfree(map_type); 283 } 284 285 /* 286 * If this process does not provide the reindexing service, turn on the 287 * forward-compatibility redirection for legacy database types and 288 * reindexing server callouts. 289 * 290 * The reindexing server should never attempt to use legacy database types, 291 * and should never be calling reindexing service client code. 292 */ 293 if (!nbdb_reindexing_lockout) 294 nbdb_rdr_init(); 295 } 296