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