Home | History | Annotate | Line # | Download | only in global
      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