Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * dfmgr.c
4 : * Dynamic function manager code.
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/fmgr/dfmgr.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <sys/stat.h>
18 :
19 : #ifndef WIN32
20 : #include <dlfcn.h>
21 : #endif /* !WIN32 */
22 :
23 : #include "fmgr.h"
24 : #include "lib/stringinfo.h"
25 : #include "miscadmin.h"
26 : #include "storage/fd.h"
27 : #include "storage/shmem.h"
28 : #include "utils/hsearch.h"
29 :
30 :
31 : /* signature for PostgreSQL-specific library init function */
32 : typedef void (*PG_init_t) (void);
33 :
34 : /* hashtable entry for rendezvous variables */
35 : typedef struct
36 : {
37 : char varName[NAMEDATALEN]; /* hash key (must be first) */
38 : void *varValue;
39 : } rendezvousHashEntry;
40 :
41 : /*
42 : * List of dynamically loaded files (kept in malloc'd memory).
43 : *
44 : * Note: "typedef struct DynamicFileList DynamicFileList" appears in fmgr.h.
45 : */
46 : struct DynamicFileList
47 : {
48 : DynamicFileList *next; /* List link */
49 : dev_t device; /* Device file is on */
50 : #ifndef WIN32 /* ensures we never again depend on this under
51 : * win32 */
52 : ino_t inode; /* Inode number of file */
53 : #endif
54 : void *handle; /* a handle for pg_dl* functions */
55 : const Pg_magic_struct *magic; /* Location of module's magic block */
56 : char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
57 : };
58 :
59 : static DynamicFileList *file_list = NULL;
60 : static DynamicFileList *file_tail = NULL;
61 :
62 : /* stat() call under Win32 returns an st_ino field, but it has no meaning */
63 : #ifndef WIN32
64 : #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
65 : #else
66 : #define SAME_INODE(A,B) false
67 : #endif
68 :
69 : char *Dynamic_library_path;
70 :
71 : static void *internal_load_library(const char *libname);
72 : pg_noreturn static void incompatible_module_error(const char *libname,
73 : const Pg_abi_values *module_magic_data);
74 : static char *expand_dynamic_library_name(const char *name);
75 : static void check_restricted_library_name(const char *name);
76 :
77 : /* ABI values that module needs to match to be accepted */
78 : static const Pg_abi_values magic_data = PG_MODULE_ABI_DATA;
79 :
80 :
81 : /*
82 : * Load the specified dynamic-link library file, and look for a function
83 : * named funcname in it.
84 : *
85 : * If the function is not found, we raise an error if signalNotFound is true,
86 : * else return NULL. Note that errors in loading the library
87 : * will provoke ereport() regardless of signalNotFound.
88 : *
89 : * If filehandle is not NULL, then *filehandle will be set to a handle
90 : * identifying the library file. The filehandle can be used with
91 : * lookup_external_function to lookup additional functions in the same file
92 : * at less cost than repeating load_external_function.
93 : */
94 : void *
95 17086 : load_external_function(const char *filename, const char *funcname,
96 : bool signalNotFound, void **filehandle)
97 : {
98 : char *fullname;
99 : void *lib_handle;
100 : void *retval;
101 :
102 : /*
103 : * For extensions with hardcoded '$libdir/' library names, we strip the
104 : * prefix to allow the library search path to be used. This is done only
105 : * for simple names (e.g., "$libdir/foo"), not for nested paths (e.g.,
106 : * "$libdir/foo/bar").
107 : *
108 : * For nested paths, 'expand_dynamic_library_name' directly expands the
109 : * '$libdir' macro, so we leave them untouched.
110 : */
111 17086 : if (strncmp(filename, "$libdir/", 8) == 0)
112 : {
113 13998 : if (first_dir_separator(filename + 8) == NULL)
114 13998 : filename += 8;
115 : }
116 :
117 : /* Expand the possibly-abbreviated filename to an exact path name */
118 17086 : fullname = expand_dynamic_library_name(filename);
119 :
120 : /* Load the shared library, unless we already did */
121 17086 : lib_handle = internal_load_library(fullname);
122 :
123 : /* Return handle if caller wants it */
124 17078 : if (filehandle)
125 14252 : *filehandle = lib_handle;
126 :
127 : /* Look up the function within the library. */
128 17078 : retval = dlsym(lib_handle, funcname);
129 :
130 17078 : if (retval == NULL && signalNotFound)
131 6 : ereport(ERROR,
132 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
133 : errmsg("could not find function \"%s\" in file \"%s\"",
134 : funcname, fullname)));
135 :
136 17072 : pfree(fullname);
137 17072 : return retval;
138 : }
139 :
140 : /*
141 : * This function loads a shlib file without looking up any particular
142 : * function in it. If the same shlib has previously been loaded,
143 : * we do not load it again.
144 : *
145 : * When 'restricted' is true, only libraries in the presumed-secure
146 : * directory $libdir/plugins may be referenced.
147 : */
148 : void
149 2752 : load_file(const char *filename, bool restricted)
150 : {
151 : char *fullname;
152 :
153 : /* Apply security restriction if requested */
154 2752 : if (restricted)
155 0 : check_restricted_library_name(filename);
156 :
157 : /* Expand the possibly-abbreviated filename to an exact path name */
158 2752 : fullname = expand_dynamic_library_name(filename);
159 :
160 : /* Load the shared library, unless we already did */
161 2752 : (void) internal_load_library(fullname);
162 :
163 2752 : pfree(fullname);
164 2752 : }
165 :
166 : /*
167 : * Lookup a function whose library file is already loaded.
168 : * Return NULL if not found.
169 : */
170 : void *
171 14246 : lookup_external_function(void *filehandle, const char *funcname)
172 : {
173 14246 : return dlsym(filehandle, funcname);
174 : }
175 :
176 :
177 : /*
178 : * Load the specified dynamic-link library file, unless it already is
179 : * loaded. Return the pg_dl* handle for the file.
180 : *
181 : * Note: libname is expected to be an exact name for the library file.
182 : *
183 : * NB: There is presently no way to unload a dynamically loaded file. We might
184 : * add one someday if we can convince ourselves we have safe protocols for un-
185 : * hooking from hook function pointers, releasing custom GUC variables, and
186 : * perhaps other things that are definitely unsafe currently.
187 : */
188 : static void *
189 24740 : internal_load_library(const char *libname)
190 : {
191 : DynamicFileList *file_scanner;
192 : PGModuleMagicFunction magic_func;
193 : char *load_error;
194 : struct stat stat_buf;
195 : PG_init_t PG_init;
196 :
197 : /*
198 : * Scan the list of loaded FILES to see if the file has been loaded.
199 : */
200 24740 : for (file_scanner = file_list;
201 36256 : file_scanner != NULL &&
202 24674 : strcmp(libname, file_scanner->filename) != 0;
203 11516 : file_scanner = file_scanner->next)
204 : ;
205 :
206 24740 : if (file_scanner == NULL)
207 : {
208 : /*
209 : * Check for same files - different paths (ie, symlink or link)
210 : */
211 11582 : if (stat(libname, &stat_buf) == -1)
212 8 : ereport(ERROR,
213 : (errcode_for_file_access(),
214 : errmsg("could not access file \"%s\": %m",
215 : libname)));
216 :
217 11574 : for (file_scanner = file_list;
218 18000 : file_scanner != NULL &&
219 6426 : !SAME_INODE(stat_buf, *file_scanner);
220 6426 : file_scanner = file_scanner->next)
221 : ;
222 : }
223 :
224 24732 : if (file_scanner == NULL)
225 : {
226 : /*
227 : * File not loaded yet.
228 : */
229 : file_scanner = (DynamicFileList *)
230 11574 : malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
231 11574 : if (file_scanner == NULL)
232 0 : ereport(ERROR,
233 : (errcode(ERRCODE_OUT_OF_MEMORY),
234 : errmsg("out of memory")));
235 :
236 69444 : MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
237 11574 : strcpy(file_scanner->filename, libname);
238 11574 : file_scanner->device = stat_buf.st_dev;
239 : #ifndef WIN32
240 11574 : file_scanner->inode = stat_buf.st_ino;
241 : #endif
242 11574 : file_scanner->next = NULL;
243 :
244 11574 : file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
245 11574 : if (file_scanner->handle == NULL)
246 : {
247 0 : load_error = dlerror();
248 0 : free(file_scanner);
249 : /* errcode_for_file_access might not be appropriate here? */
250 0 : ereport(ERROR,
251 : (errcode_for_file_access(),
252 : errmsg("could not load library \"%s\": %s",
253 : libname, load_error)));
254 : }
255 :
256 : /* Check the magic function to determine compatibility */
257 11574 : magic_func = (PGModuleMagicFunction)
258 11574 : dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
259 11574 : if (magic_func)
260 : {
261 11574 : const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
262 :
263 : /* Check ABI compatibility fields */
264 11574 : if (magic_data_ptr->len != sizeof(Pg_magic_struct) ||
265 11574 : memcmp(&magic_data_ptr->abi_fields, &magic_data,
266 : sizeof(Pg_abi_values)) != 0)
267 : {
268 : /* copy data block before unlinking library */
269 0 : Pg_magic_struct module_magic_data = *magic_data_ptr;
270 :
271 : /* try to close library */
272 0 : dlclose(file_scanner->handle);
273 0 : free(file_scanner);
274 :
275 : /* issue suitable complaint */
276 0 : incompatible_module_error(libname, &module_magic_data.abi_fields);
277 : }
278 :
279 : /* Remember the magic block's location for future use */
280 11574 : file_scanner->magic = magic_data_ptr;
281 : }
282 : else
283 : {
284 : /* try to close library */
285 0 : dlclose(file_scanner->handle);
286 0 : free(file_scanner);
287 : /* complain */
288 0 : ereport(ERROR,
289 : (errmsg("incompatible library \"%s\": missing magic block",
290 : libname),
291 : errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
292 : }
293 :
294 : /*
295 : * If the library has a _PG_init() function, call it.
296 : */
297 11574 : PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
298 11574 : if (PG_init)
299 6462 : (*PG_init) ();
300 :
301 : /* OK to link it into list */
302 11574 : if (file_list == NULL)
303 8096 : file_list = file_scanner;
304 : else
305 3478 : file_tail->next = file_scanner;
306 11574 : file_tail = file_scanner;
307 : }
308 :
309 24732 : return file_scanner->handle;
310 : }
311 :
312 : /*
313 : * Report a suitable error for an incompatible magic block.
314 : */
315 : static void
316 0 : incompatible_module_error(const char *libname,
317 : const Pg_abi_values *module_magic_data)
318 : {
319 : StringInfoData details;
320 :
321 : /*
322 : * If the version doesn't match, just report that, because the rest of the
323 : * block might not even have the fields we expect.
324 : */
325 0 : if (magic_data.version != module_magic_data->version)
326 : {
327 : char library_version[32];
328 :
329 0 : if (module_magic_data->version >= 1000)
330 0 : snprintf(library_version, sizeof(library_version), "%d",
331 0 : module_magic_data->version / 100);
332 : else
333 0 : snprintf(library_version, sizeof(library_version), "%d.%d",
334 0 : module_magic_data->version / 100,
335 0 : module_magic_data->version % 100);
336 0 : ereport(ERROR,
337 : (errmsg("incompatible library \"%s\": version mismatch",
338 : libname),
339 : errdetail("Server is version %d, library is version %s.",
340 : magic_data.version / 100, library_version)));
341 : }
342 :
343 : /*
344 : * Similarly, if the ABI extra field doesn't match, error out. Other
345 : * fields below might also mismatch, but that isn't useful information if
346 : * you're using the wrong product altogether.
347 : */
348 0 : if (strcmp(module_magic_data->abi_extra, magic_data.abi_extra) != 0)
349 : {
350 0 : ereport(ERROR,
351 : (errmsg("incompatible library \"%s\": ABI mismatch",
352 : libname),
353 : errdetail("Server has ABI \"%s\", library has \"%s\".",
354 : magic_data.abi_extra,
355 : module_magic_data->abi_extra)));
356 : }
357 :
358 : /*
359 : * Otherwise, spell out which fields don't agree.
360 : *
361 : * XXX this code has to be adjusted any time the set of fields in a magic
362 : * block change!
363 : */
364 0 : initStringInfo(&details);
365 :
366 0 : if (module_magic_data->funcmaxargs != magic_data.funcmaxargs)
367 : {
368 0 : if (details.len)
369 0 : appendStringInfoChar(&details, '\n');
370 0 : appendStringInfo(&details,
371 : /* translator: %s is a variable name and %d its values */
372 0 : _("Server has %s = %d, library has %d."),
373 0 : "FUNC_MAX_ARGS", magic_data.funcmaxargs,
374 0 : module_magic_data->funcmaxargs);
375 : }
376 0 : if (module_magic_data->indexmaxkeys != magic_data.indexmaxkeys)
377 : {
378 0 : if (details.len)
379 0 : appendStringInfoChar(&details, '\n');
380 0 : appendStringInfo(&details,
381 : /* translator: %s is a variable name and %d its values */
382 0 : _("Server has %s = %d, library has %d."),
383 0 : "INDEX_MAX_KEYS", magic_data.indexmaxkeys,
384 0 : module_magic_data->indexmaxkeys);
385 : }
386 0 : if (module_magic_data->namedatalen != magic_data.namedatalen)
387 : {
388 0 : if (details.len)
389 0 : appendStringInfoChar(&details, '\n');
390 0 : appendStringInfo(&details,
391 : /* translator: %s is a variable name and %d its values */
392 0 : _("Server has %s = %d, library has %d."),
393 0 : "NAMEDATALEN", magic_data.namedatalen,
394 0 : module_magic_data->namedatalen);
395 : }
396 0 : if (module_magic_data->float8byval != magic_data.float8byval)
397 : {
398 0 : if (details.len)
399 0 : appendStringInfoChar(&details, '\n');
400 0 : appendStringInfo(&details,
401 : /* translator: %s is a variable name and %d its values */
402 0 : _("Server has %s = %s, library has %s."),
403 0 : "FLOAT8PASSBYVAL", magic_data.float8byval ? "true" : "false",
404 0 : module_magic_data->float8byval ? "true" : "false");
405 : }
406 :
407 0 : if (details.len == 0)
408 0 : appendStringInfoString(&details,
409 0 : _("Magic block has unexpected length or padding difference."));
410 :
411 0 : ereport(ERROR,
412 : (errmsg("incompatible library \"%s\": magic block mismatch",
413 : libname),
414 : errdetail_internal("%s", details.data)));
415 : }
416 :
417 :
418 : /*
419 : * Iterator functions to allow callers to scan the list of loaded modules.
420 : *
421 : * Note: currently, there is no special provision for dealing with changes
422 : * in the list while a scan is happening. Current callers don't need it.
423 : */
424 : DynamicFileList *
425 0 : get_first_loaded_module(void)
426 : {
427 0 : return file_list;
428 : }
429 :
430 : DynamicFileList *
431 0 : get_next_loaded_module(DynamicFileList *dfptr)
432 : {
433 0 : return dfptr->next;
434 : }
435 :
436 : /*
437 : * Return some details about the specified module.
438 : *
439 : * Note that module_name and module_version could be returned as NULL.
440 : *
441 : * We could dispense with this function by exposing struct DynamicFileList
442 : * globally, but this way seems preferable.
443 : */
444 : void
445 0 : get_loaded_module_details(DynamicFileList *dfptr,
446 : const char **library_path,
447 : const char **module_name,
448 : const char **module_version)
449 : {
450 0 : *library_path = dfptr->filename;
451 0 : *module_name = dfptr->magic->name;
452 0 : *module_version = dfptr->magic->version;
453 0 : }
454 :
455 :
456 : /*
457 : * If name contains a slash, check if the file exists, if so return
458 : * the name. Else (no slash) try to expand using search path (see
459 : * find_in_path below); if that works, return the fully
460 : * expanded file name. If the previous failed, append DLSUFFIX and
461 : * try again. If all fails, just return the original name.
462 : *
463 : * The result will always be freshly palloc'd.
464 : */
465 : static char *
466 19838 : expand_dynamic_library_name(const char *name)
467 : {
468 : bool have_slash;
469 : char *new;
470 : char *full;
471 :
472 : Assert(name);
473 :
474 19838 : have_slash = (first_dir_separator(name) != NULL);
475 :
476 19838 : if (!have_slash)
477 : {
478 18900 : full = find_in_path(name, Dynamic_library_path, "dynamic_library_path", "$libdir", pkglib_path);
479 18900 : if (full)
480 0 : return full;
481 : }
482 : else
483 : {
484 938 : full = substitute_path_macro(name, "$libdir", pkglib_path);
485 938 : if (pg_file_exists(full))
486 938 : return full;
487 0 : pfree(full);
488 : }
489 :
490 18900 : new = psprintf("%s%s", name, DLSUFFIX);
491 :
492 18900 : if (!have_slash)
493 : {
494 18900 : full = find_in_path(new, Dynamic_library_path, "dynamic_library_path", "$libdir", pkglib_path);
495 18900 : pfree(new);
496 18900 : if (full)
497 18892 : return full;
498 : }
499 : else
500 : {
501 0 : full = substitute_path_macro(new, "$libdir", pkglib_path);
502 0 : pfree(new);
503 0 : if (pg_file_exists(full))
504 0 : return full;
505 0 : pfree(full);
506 : }
507 :
508 : /*
509 : * If we can't find the file, just return the string as-is. The ensuing
510 : * load attempt will fail and report a suitable message.
511 : */
512 8 : return pstrdup(name);
513 : }
514 :
515 : /*
516 : * Check a restricted library name. It must begin with "$libdir/plugins/"
517 : * and there must not be any directory separators after that (this is
518 : * sufficient to prevent ".." style attacks).
519 : */
520 : static void
521 0 : check_restricted_library_name(const char *name)
522 : {
523 0 : if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
524 0 : first_dir_separator(name + 16) != NULL)
525 0 : ereport(ERROR,
526 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
527 : errmsg("access to library \"%s\" is not allowed",
528 : name)));
529 0 : }
530 :
531 : /*
532 : * Substitute for any macros appearing in the given string.
533 : * Result is always freshly palloc'd.
534 : */
535 : char *
536 39460 : substitute_path_macro(const char *str, const char *macro, const char *value)
537 : {
538 : const char *sep_ptr;
539 :
540 : Assert(str != NULL);
541 : Assert(macro[0] == '$');
542 :
543 : /* Currently, we only recognize $macro at the start of the string */
544 39460 : if (str[0] != '$')
545 938 : return pstrdup(str);
546 :
547 38522 : if ((sep_ptr = first_dir_separator(str)) == NULL)
548 38522 : sep_ptr = str + strlen(str);
549 :
550 38522 : if (strlen(macro) != sep_ptr - str ||
551 38522 : strncmp(str, macro, strlen(macro)) != 0)
552 0 : ereport(ERROR,
553 : (errcode(ERRCODE_INVALID_NAME),
554 : errmsg("invalid macro name in path: %s",
555 : str)));
556 :
557 38522 : return psprintf("%s%s", value, sep_ptr);
558 : }
559 :
560 :
561 : /*
562 : * Search for a file called 'basename' in the colon-separated search
563 : * path given. If the file is found, the full file name
564 : * is returned in freshly palloc'd memory. If the file is not found,
565 : * return NULL.
566 : *
567 : * path_param is the name of the parameter that path came from, for error
568 : * messages.
569 : *
570 : * macro and macro_val allow substituting a macro; see
571 : * substitute_path_macro().
572 : */
573 : char *
574 37800 : find_in_path(const char *basename, const char *path, const char *path_param,
575 : const char *macro, const char *macro_val)
576 : {
577 : const char *p;
578 : size_t baselen;
579 :
580 : Assert(basename != NULL);
581 : Assert(first_dir_separator(basename) == NULL);
582 : Assert(path != NULL);
583 : Assert(path_param != NULL);
584 :
585 37800 : p = path;
586 :
587 : /*
588 : * If the path variable is empty, don't do a path search.
589 : */
590 37800 : if (strlen(p) == 0)
591 0 : return NULL;
592 :
593 37800 : baselen = strlen(basename);
594 :
595 : for (;;)
596 0 : {
597 : size_t len;
598 : char *piece;
599 : char *mangled;
600 : char *full;
601 :
602 37800 : piece = first_path_var_separator(p);
603 37800 : if (piece == p)
604 0 : ereport(ERROR,
605 : (errcode(ERRCODE_INVALID_NAME),
606 : errmsg("zero-length component in parameter \"%s\"", path_param)));
607 :
608 37800 : if (piece == NULL)
609 37800 : len = strlen(p);
610 : else
611 0 : len = piece - p;
612 :
613 37800 : piece = palloc(len + 1);
614 37800 : strlcpy(piece, p, len + 1);
615 :
616 37800 : mangled = substitute_path_macro(piece, macro, macro_val);
617 37800 : pfree(piece);
618 :
619 37800 : canonicalize_path(mangled);
620 :
621 : /* only absolute paths */
622 37800 : if (!is_absolute_path(mangled))
623 0 : ereport(ERROR,
624 : (errcode(ERRCODE_INVALID_NAME),
625 : errmsg("component in parameter \"%s\" is not an absolute path", path_param)));
626 :
627 37800 : full = palloc(strlen(mangled) + 1 + baselen + 1);
628 37800 : sprintf(full, "%s/%s", mangled, basename);
629 37800 : pfree(mangled);
630 :
631 37800 : elog(DEBUG3, "%s: trying \"%s\"", __func__, full);
632 :
633 37800 : if (pg_file_exists(full))
634 18892 : return full;
635 :
636 18908 : pfree(full);
637 :
638 18908 : if (p[len] == '\0')
639 18908 : break;
640 : else
641 0 : p += len + 1;
642 : }
643 :
644 18908 : return NULL;
645 : }
646 :
647 :
648 : /*
649 : * Find (or create) a rendezvous variable that one dynamically
650 : * loaded library can use to meet up with another.
651 : *
652 : * On the first call of this function for a particular varName,
653 : * a "rendezvous variable" is created with the given name.
654 : * The value of the variable is a void pointer (initially set to NULL).
655 : * Subsequent calls with the same varName just return the address of
656 : * the existing variable. Once created, a rendezvous variable lasts
657 : * for the life of the process.
658 : *
659 : * Dynamically loaded libraries can use rendezvous variables
660 : * to find each other and share information: they just need to agree
661 : * on the variable name and the data it will point to.
662 : */
663 : void **
664 3610 : find_rendezvous_variable(const char *varName)
665 : {
666 : static HTAB *rendezvousHash = NULL;
667 :
668 : rendezvousHashEntry *hentry;
669 : bool found;
670 :
671 : /* Create a hashtable if we haven't already done so in this process */
672 3610 : if (rendezvousHash == NULL)
673 : {
674 : HASHCTL ctl;
675 :
676 3604 : ctl.keysize = NAMEDATALEN;
677 3604 : ctl.entrysize = sizeof(rendezvousHashEntry);
678 3604 : rendezvousHash = hash_create("Rendezvous variable hash",
679 : 16,
680 : &ctl,
681 : HASH_ELEM | HASH_STRINGS);
682 : }
683 :
684 : /* Find or create the hashtable entry for this varName */
685 3610 : hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
686 : varName,
687 : HASH_ENTER,
688 : &found);
689 :
690 : /* Initialize to NULL if first time */
691 3610 : if (!found)
692 3610 : hentry->varValue = NULL;
693 :
694 3610 : return &hentry->varValue;
695 : }
696 :
697 : /*
698 : * Estimate the amount of space needed to serialize the list of libraries
699 : * we have loaded.
700 : */
701 : Size
702 910 : EstimateLibraryStateSpace(void)
703 : {
704 : DynamicFileList *file_scanner;
705 910 : Size size = 1;
706 :
707 910 : for (file_scanner = file_list;
708 2238 : file_scanner != NULL;
709 1328 : file_scanner = file_scanner->next)
710 1328 : size = add_size(size, strlen(file_scanner->filename) + 1);
711 :
712 910 : return size;
713 : }
714 :
715 : /*
716 : * Serialize the list of libraries we have loaded to a chunk of memory.
717 : */
718 : void
719 910 : SerializeLibraryState(Size maxsize, char *start_address)
720 : {
721 : DynamicFileList *file_scanner;
722 :
723 910 : for (file_scanner = file_list;
724 2238 : file_scanner != NULL;
725 1328 : file_scanner = file_scanner->next)
726 : {
727 : Size len;
728 :
729 1328 : len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
730 : Assert(len < maxsize);
731 1328 : maxsize -= len;
732 1328 : start_address += len;
733 : }
734 910 : start_address[0] = '\0';
735 910 : }
736 :
737 : /*
738 : * Load every library the serializing backend had loaded.
739 : */
740 : void
741 2734 : RestoreLibraryState(char *start_address)
742 : {
743 7636 : while (*start_address != '\0')
744 : {
745 4902 : internal_load_library(start_address);
746 4902 : start_address += strlen(start_address) + 1;
747 : }
748 2734 : }
|