Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * genfile.c
4 : * Functions for direct access to files
5 : *
6 : *
7 : * Copyright (c) 2004-2022, PostgreSQL Global Development Group
8 : *
9 : * Author: Andreas Pflug <pgadmin@pse-consulting.de>
10 : *
11 : * IDENTIFICATION
12 : * src/backend/utils/adt/genfile.c
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 : #include "postgres.h"
17 :
18 : #include <sys/file.h>
19 : #include <sys/stat.h>
20 : #include <unistd.h>
21 : #include <dirent.h>
22 :
23 : #include "access/htup_details.h"
24 : #include "access/xlog_internal.h"
25 : #include "catalog/pg_authid.h"
26 : #include "catalog/pg_tablespace_d.h"
27 : #include "catalog/pg_type.h"
28 : #include "funcapi.h"
29 : #include "mb/pg_wchar.h"
30 : #include "miscadmin.h"
31 : #include "postmaster/syslogger.h"
32 : #include "replication/slot.h"
33 : #include "storage/fd.h"
34 : #include "utils/acl.h"
35 : #include "utils/builtins.h"
36 : #include "utils/memutils.h"
37 : #include "utils/syscache.h"
38 : #include "utils/timestamp.h"
39 :
40 :
41 : /*
42 : * Convert a "text" filename argument to C string, and check it's allowable.
43 : *
44 : * Filename may be absolute or relative to the DataDir, but we only allow
45 : * absolute paths that match DataDir or Log_directory.
46 : *
47 : * This does a privilege check against the 'pg_read_server_files' role, so
48 : * this function is really only appropriate for callers who are only checking
49 : * 'read' access. Do not use this function if you are looking for a check
50 : * for 'write' or 'program' access without updating it to access the type
51 : * of check as an argument and checking the appropriate role membership.
52 : */
53 : static char *
54 19126 : convert_and_check_filename(text *arg)
55 : {
56 : char *filename;
57 :
58 19126 : filename = text_to_cstring(arg);
59 19126 : canonicalize_path(filename); /* filename can change length here */
60 :
61 : /*
62 : * Roles with privileges of the 'pg_read_server_files' role are allowed to
63 : * access any files on the server as the PG user, so no need to do any
64 : * further checks here.
65 : */
66 19126 : if (has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
67 5426 : return filename;
68 :
69 : /*
70 : * User isn't a member of the pg_read_server_files role, so check if it's
71 : * allowable
72 : */
73 13700 : if (is_absolute_path(filename))
74 : {
75 : /*
76 : * Allow absolute paths if within DataDir or Log_directory, even
77 : * though Log_directory might be outside DataDir.
78 : */
79 0 : if (!path_is_prefix_of_path(DataDir, filename) &&
80 0 : (!is_absolute_path(Log_directory) ||
81 0 : !path_is_prefix_of_path(Log_directory, filename)))
82 0 : ereport(ERROR,
83 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
84 : errmsg("absolute path not allowed")));
85 : }
86 13700 : else if (!path_is_relative_and_below_cwd(filename))
87 0 : ereport(ERROR,
88 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
89 : errmsg("path must be in or below the current directory")));
90 :
91 13700 : return filename;
92 : }
93 :
94 :
95 : /*
96 : * Read a section of a file, returning it as bytea
97 : *
98 : * Caller is responsible for all permissions checking.
99 : *
100 : * We read the whole of the file when bytes_to_read is negative.
101 : */
102 : static bytea *
103 4882 : read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
104 : bool missing_ok)
105 : {
106 : bytea *buf;
107 4882 : size_t nbytes = 0;
108 : FILE *file;
109 :
110 : /* clamp request size to what we can actually deliver */
111 4882 : if (bytes_to_read > (int64) (MaxAllocSize - VARHDRSZ))
112 0 : ereport(ERROR,
113 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
114 : errmsg("requested length too large")));
115 :
116 4882 : if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
117 : {
118 4 : if (missing_ok && errno == ENOENT)
119 0 : return NULL;
120 : else
121 4 : ereport(ERROR,
122 : (errcode_for_file_access(),
123 : errmsg("could not open file \"%s\" for reading: %m",
124 : filename)));
125 : }
126 :
127 4878 : if (fseeko(file, (off_t) seek_offset,
128 : (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
129 0 : ereport(ERROR,
130 : (errcode_for_file_access(),
131 : errmsg("could not seek in file \"%s\": %m", filename)));
132 :
133 4878 : if (bytes_to_read >= 0)
134 : {
135 : /* If passed explicit read size just do it */
136 4832 : buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
137 :
138 4832 : nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
139 : }
140 : else
141 : {
142 : /* Negative read size, read rest of file */
143 : StringInfoData sbuf;
144 :
145 46 : initStringInfo(&sbuf);
146 : /* Leave room in the buffer for the varlena length word */
147 46 : sbuf.len += VARHDRSZ;
148 : Assert(sbuf.len < sbuf.maxlen);
149 :
150 116 : while (!(feof(file) || ferror(file)))
151 : {
152 : size_t rbytes;
153 :
154 : /* Minimum amount to read at a time */
155 : #define MIN_READ_SIZE 4096
156 :
157 : /*
158 : * If not at end of file, and sbuf.len is equal to MaxAllocSize -
159 : * 1, then either the file is too large, or there is nothing left
160 : * to read. Attempt to read one more byte to see if the end of
161 : * file has been reached. If not, the file is too large; we'd
162 : * rather give the error message for that ourselves.
163 : */
164 70 : if (sbuf.len == MaxAllocSize - 1)
165 : {
166 : char rbuf[1];
167 :
168 0 : if (fread(rbuf, 1, 1, file) != 0 || !feof(file))
169 0 : ereport(ERROR,
170 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
171 : errmsg("file length too large")));
172 : else
173 : break;
174 : }
175 :
176 : /* OK, ensure that we can read at least MIN_READ_SIZE */
177 70 : enlargeStringInfo(&sbuf, MIN_READ_SIZE);
178 :
179 : /*
180 : * stringinfo.c likes to allocate in powers of 2, so it's likely
181 : * that much more space is available than we asked for. Use all
182 : * of it, rather than making more fread calls than necessary.
183 : */
184 70 : rbytes = fread(sbuf.data + sbuf.len, 1,
185 70 : (size_t) (sbuf.maxlen - sbuf.len - 1), file);
186 70 : sbuf.len += rbytes;
187 70 : nbytes += rbytes;
188 : }
189 :
190 : /* Now we can commandeer the stringinfo's buffer as the result */
191 46 : buf = (bytea *) sbuf.data;
192 : }
193 :
194 4878 : if (ferror(file))
195 0 : ereport(ERROR,
196 : (errcode_for_file_access(),
197 : errmsg("could not read file \"%s\": %m", filename)));
198 :
199 4878 : SET_VARSIZE(buf, nbytes + VARHDRSZ);
200 :
201 4878 : FreeFile(file);
202 :
203 4878 : return buf;
204 : }
205 :
206 : /*
207 : * Similar to read_binary_file, but we verify that the contents are valid
208 : * in the database encoding.
209 : */
210 : static text *
211 16 : read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
212 : bool missing_ok)
213 : {
214 : bytea *buf;
215 :
216 16 : buf = read_binary_file(filename, seek_offset, bytes_to_read, missing_ok);
217 :
218 12 : if (buf != NULL)
219 : {
220 : /* Make sure the input is valid */
221 12 : pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
222 :
223 : /* OK, we can cast it to text safely */
224 12 : return (text *) buf;
225 : }
226 : else
227 0 : return NULL;
228 : }
229 :
230 : /*
231 : * Read a section of a file, returning it as text
232 : *
233 : * This function is kept to support adminpack 1.0.
234 : */
235 : Datum
236 0 : pg_read_file(PG_FUNCTION_ARGS)
237 : {
238 0 : text *filename_t = PG_GETARG_TEXT_PP(0);
239 0 : int64 seek_offset = 0;
240 0 : int64 bytes_to_read = -1;
241 0 : bool missing_ok = false;
242 : char *filename;
243 : text *result;
244 :
245 0 : if (!superuser())
246 0 : ereport(ERROR,
247 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
248 : errmsg("must be superuser to read files with adminpack 1.0"),
249 : /* translator: %s is a SQL function name */
250 : errhint("Consider using %s, which is part of core, instead.",
251 : "pg_read_file()")));
252 :
253 : /* handle optional arguments */
254 0 : if (PG_NARGS() >= 3)
255 : {
256 0 : seek_offset = PG_GETARG_INT64(1);
257 0 : bytes_to_read = PG_GETARG_INT64(2);
258 :
259 0 : if (bytes_to_read < 0)
260 0 : ereport(ERROR,
261 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
262 : errmsg("requested length cannot be negative")));
263 : }
264 0 : if (PG_NARGS() >= 4)
265 0 : missing_ok = PG_GETARG_BOOL(3);
266 :
267 0 : filename = convert_and_check_filename(filename_t);
268 :
269 0 : result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
270 0 : if (result)
271 0 : PG_RETURN_TEXT_P(result);
272 : else
273 0 : PG_RETURN_NULL();
274 : }
275 :
276 : /*
277 : * Read a section of a file, returning it as text
278 : *
279 : * No superuser check done here- instead privileges are handled by the
280 : * GRANT system.
281 : */
282 : Datum
283 16 : pg_read_file_v2(PG_FUNCTION_ARGS)
284 : {
285 16 : text *filename_t = PG_GETARG_TEXT_PP(0);
286 16 : int64 seek_offset = 0;
287 16 : int64 bytes_to_read = -1;
288 16 : bool missing_ok = false;
289 : char *filename;
290 : text *result;
291 :
292 : /* handle optional arguments */
293 16 : if (PG_NARGS() >= 3)
294 : {
295 0 : seek_offset = PG_GETARG_INT64(1);
296 0 : bytes_to_read = PG_GETARG_INT64(2);
297 :
298 0 : if (bytes_to_read < 0)
299 0 : ereport(ERROR,
300 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
301 : errmsg("requested length cannot be negative")));
302 : }
303 16 : if (PG_NARGS() >= 4)
304 0 : missing_ok = PG_GETARG_BOOL(3);
305 :
306 16 : filename = convert_and_check_filename(filename_t);
307 :
308 16 : result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
309 12 : if (result)
310 12 : PG_RETURN_TEXT_P(result);
311 : else
312 0 : PG_RETURN_NULL();
313 : }
314 :
315 : /*
316 : * Read a section of a file, returning it as bytea
317 : */
318 : Datum
319 4866 : pg_read_binary_file(PG_FUNCTION_ARGS)
320 : {
321 4866 : text *filename_t = PG_GETARG_TEXT_PP(0);
322 4866 : int64 seek_offset = 0;
323 4866 : int64 bytes_to_read = -1;
324 4866 : bool missing_ok = false;
325 : char *filename;
326 : bytea *result;
327 :
328 : /* handle optional arguments */
329 4866 : if (PG_NARGS() >= 3)
330 : {
331 4832 : seek_offset = PG_GETARG_INT64(1);
332 4832 : bytes_to_read = PG_GETARG_INT64(2);
333 :
334 4832 : if (bytes_to_read < 0)
335 0 : ereport(ERROR,
336 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
337 : errmsg("requested length cannot be negative")));
338 : }
339 4866 : if (PG_NARGS() >= 4)
340 4832 : missing_ok = PG_GETARG_BOOL(3);
341 :
342 4866 : filename = convert_and_check_filename(filename_t);
343 :
344 4866 : result = read_binary_file(filename, seek_offset,
345 : bytes_to_read, missing_ok);
346 4866 : if (result)
347 4866 : PG_RETURN_BYTEA_P(result);
348 : else
349 0 : PG_RETURN_NULL();
350 : }
351 :
352 :
353 : /*
354 : * Wrapper functions for the 1 and 3 argument variants of pg_read_file_v2()
355 : * and pg_read_binary_file().
356 : *
357 : * These are necessary to pass the sanity check in opr_sanity, which checks
358 : * that all built-in functions that share the implementing C function take
359 : * the same number of arguments.
360 : */
361 : Datum
362 0 : pg_read_file_off_len(PG_FUNCTION_ARGS)
363 : {
364 0 : return pg_read_file_v2(fcinfo);
365 : }
366 :
367 : Datum
368 16 : pg_read_file_all(PG_FUNCTION_ARGS)
369 : {
370 16 : return pg_read_file_v2(fcinfo);
371 : }
372 :
373 : Datum
374 0 : pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
375 : {
376 0 : return pg_read_binary_file(fcinfo);
377 : }
378 :
379 : Datum
380 34 : pg_read_binary_file_all(PG_FUNCTION_ARGS)
381 : {
382 34 : return pg_read_binary_file(fcinfo);
383 : }
384 :
385 : /*
386 : * stat a file
387 : */
388 : Datum
389 13870 : pg_stat_file(PG_FUNCTION_ARGS)
390 : {
391 13870 : text *filename_t = PG_GETARG_TEXT_PP(0);
392 : char *filename;
393 : struct stat fst;
394 : Datum values[6];
395 : bool isnull[6];
396 : HeapTuple tuple;
397 : TupleDesc tupdesc;
398 13870 : bool missing_ok = false;
399 :
400 : /* check the optional argument */
401 13870 : if (PG_NARGS() == 2)
402 13866 : missing_ok = PG_GETARG_BOOL(1);
403 :
404 13870 : filename = convert_and_check_filename(filename_t);
405 :
406 13870 : if (stat(filename, &fst) < 0)
407 : {
408 2 : if (missing_ok && errno == ENOENT)
409 0 : PG_RETURN_NULL();
410 : else
411 2 : ereport(ERROR,
412 : (errcode_for_file_access(),
413 : errmsg("could not stat file \"%s\": %m", filename)));
414 : }
415 :
416 : /*
417 : * This record type had better match the output parameters declared for me
418 : * in pg_proc.h.
419 : */
420 13868 : tupdesc = CreateTemplateTupleDesc(6);
421 13868 : TupleDescInitEntry(tupdesc, (AttrNumber) 1,
422 : "size", INT8OID, -1, 0);
423 13868 : TupleDescInitEntry(tupdesc, (AttrNumber) 2,
424 : "access", TIMESTAMPTZOID, -1, 0);
425 13868 : TupleDescInitEntry(tupdesc, (AttrNumber) 3,
426 : "modification", TIMESTAMPTZOID, -1, 0);
427 13868 : TupleDescInitEntry(tupdesc, (AttrNumber) 4,
428 : "change", TIMESTAMPTZOID, -1, 0);
429 13868 : TupleDescInitEntry(tupdesc, (AttrNumber) 5,
430 : "creation", TIMESTAMPTZOID, -1, 0);
431 13868 : TupleDescInitEntry(tupdesc, (AttrNumber) 6,
432 : "isdir", BOOLOID, -1, 0);
433 13868 : BlessTupleDesc(tupdesc);
434 :
435 13868 : memset(isnull, false, sizeof(isnull));
436 :
437 13868 : values[0] = Int64GetDatum((int64) fst.st_size);
438 13868 : values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
439 13868 : values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
440 : /* Unix has file status change time, while Win32 has creation time */
441 : #if !defined(WIN32) && !defined(__CYGWIN__)
442 13868 : values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
443 13868 : isnull[4] = true;
444 : #else
445 : isnull[3] = true;
446 : values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
447 : #endif
448 13868 : values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
449 :
450 13868 : tuple = heap_form_tuple(tupdesc, values, isnull);
451 :
452 13868 : pfree(filename);
453 :
454 13868 : PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
455 : }
456 :
457 : /*
458 : * stat a file (1 argument version)
459 : *
460 : * note: this wrapper is necessary to pass the sanity check in opr_sanity,
461 : * which checks that all built-in functions that share the implementing C
462 : * function take the same number of arguments
463 : */
464 : Datum
465 4 : pg_stat_file_1arg(PG_FUNCTION_ARGS)
466 : {
467 4 : return pg_stat_file(fcinfo);
468 : }
469 :
470 : /*
471 : * List a directory (returns the filenames only)
472 : */
473 : Datum
474 374 : pg_ls_dir(PG_FUNCTION_ARGS)
475 : {
476 374 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
477 : char *location;
478 374 : bool missing_ok = false;
479 374 : bool include_dot_dirs = false;
480 : DIR *dirdesc;
481 : struct dirent *de;
482 :
483 374 : location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
484 :
485 : /* check the optional arguments */
486 374 : if (PG_NARGS() == 3)
487 : {
488 360 : if (!PG_ARGISNULL(1))
489 360 : missing_ok = PG_GETARG_BOOL(1);
490 360 : if (!PG_ARGISNULL(2))
491 360 : include_dot_dirs = PG_GETARG_BOOL(2);
492 : }
493 :
494 374 : SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
495 :
496 374 : dirdesc = AllocateDir(location);
497 374 : if (!dirdesc)
498 : {
499 : /* Return empty tuplestore if appropriate */
500 12 : if (missing_ok && errno == ENOENT)
501 6 : return (Datum) 0;
502 : /* Otherwise, we can let ReadDir() throw the error */
503 : }
504 :
505 15552 : while ((de = ReadDir(dirdesc, location)) != NULL)
506 : {
507 : Datum values[1];
508 : bool nulls[1];
509 :
510 15184 : if (!include_dot_dirs &&
511 15028 : (strcmp(de->d_name, ".") == 0 ||
512 14672 : strcmp(de->d_name, "..") == 0))
513 712 : continue;
514 :
515 14472 : values[0] = CStringGetTextDatum(de->d_name);
516 14472 : nulls[0] = false;
517 :
518 14472 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
519 : values, nulls);
520 : }
521 :
522 362 : FreeDir(dirdesc);
523 362 : return (Datum) 0;
524 : }
525 :
526 : /*
527 : * List a directory (1 argument version)
528 : *
529 : * note: this wrapper is necessary to pass the sanity check in opr_sanity,
530 : * which checks that all built-in functions that share the implementing C
531 : * function take the same number of arguments.
532 : */
533 : Datum
534 14 : pg_ls_dir_1arg(PG_FUNCTION_ARGS)
535 : {
536 14 : return pg_ls_dir(fcinfo);
537 : }
538 :
539 : /*
540 : * Generic function to return a directory listing of files.
541 : *
542 : * If the directory isn't there, silently return an empty set if missing_ok.
543 : * Other unreadable-directory cases throw an error.
544 : */
545 : static Datum
546 36 : pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
547 : {
548 36 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
549 : DIR *dirdesc;
550 : struct dirent *de;
551 :
552 36 : SetSingleFuncCall(fcinfo, 0);
553 :
554 : /*
555 : * Now walk the directory. Note that we must do this within a single SRF
556 : * call, not leave the directory open across multiple calls, since we
557 : * can't count on the SRF being run to completion.
558 : */
559 36 : dirdesc = AllocateDir(dir);
560 36 : if (!dirdesc)
561 : {
562 : /* Return empty tuplestore if appropriate */
563 0 : if (missing_ok && errno == ENOENT)
564 0 : return (Datum) 0;
565 : /* Otherwise, we can let ReadDir() throw the error */
566 : }
567 :
568 940 : while ((de = ReadDir(dirdesc, dir)) != NULL)
569 : {
570 : Datum values[3];
571 : bool nulls[3];
572 : char path[MAXPGPATH * 2];
573 : struct stat attrib;
574 :
575 : /* Skip hidden files */
576 904 : if (de->d_name[0] == '.')
577 96 : continue;
578 :
579 : /* Get the file info */
580 832 : snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
581 832 : if (stat(path, &attrib) < 0)
582 : {
583 : /* Ignore concurrently-deleted files, else complain */
584 0 : if (errno == ENOENT)
585 0 : continue;
586 0 : ereport(ERROR,
587 : (errcode_for_file_access(),
588 : errmsg("could not stat file \"%s\": %m", path)));
589 : }
590 :
591 : /* Ignore anything but regular files */
592 832 : if (!S_ISREG(attrib.st_mode))
593 24 : continue;
594 :
595 808 : values[0] = CStringGetTextDatum(de->d_name);
596 808 : values[1] = Int64GetDatum((int64) attrib.st_size);
597 808 : values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
598 808 : memset(nulls, 0, sizeof(nulls));
599 :
600 808 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
601 : }
602 :
603 36 : FreeDir(dirdesc);
604 36 : return (Datum) 0;
605 : }
606 :
607 : /* Function to return the list of files in the log directory */
608 : Datum
609 0 : pg_ls_logdir(PG_FUNCTION_ARGS)
610 : {
611 0 : return pg_ls_dir_files(fcinfo, Log_directory, false);
612 : }
613 :
614 : /* Function to return the list of files in the WAL directory */
615 : Datum
616 24 : pg_ls_waldir(PG_FUNCTION_ARGS)
617 : {
618 24 : return pg_ls_dir_files(fcinfo, XLOGDIR, false);
619 : }
620 :
621 : /*
622 : * Generic function to return the list of files in pgsql_tmp
623 : */
624 : static Datum
625 0 : pg_ls_tmpdir(FunctionCallInfo fcinfo, Oid tblspc)
626 : {
627 : char path[MAXPGPATH];
628 :
629 0 : if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tblspc)))
630 0 : ereport(ERROR,
631 : (errcode(ERRCODE_UNDEFINED_OBJECT),
632 : errmsg("tablespace with OID %u does not exist",
633 : tblspc)));
634 :
635 0 : TempTablespacePath(path, tblspc);
636 0 : return pg_ls_dir_files(fcinfo, path, true);
637 : }
638 :
639 : /*
640 : * Function to return the list of temporary files in the pg_default tablespace's
641 : * pgsql_tmp directory
642 : */
643 : Datum
644 0 : pg_ls_tmpdir_noargs(PG_FUNCTION_ARGS)
645 : {
646 0 : return pg_ls_tmpdir(fcinfo, DEFAULTTABLESPACE_OID);
647 : }
648 :
649 : /*
650 : * Function to return the list of temporary files in the specified tablespace's
651 : * pgsql_tmp directory
652 : */
653 : Datum
654 0 : pg_ls_tmpdir_1arg(PG_FUNCTION_ARGS)
655 : {
656 0 : return pg_ls_tmpdir(fcinfo, PG_GETARG_OID(0));
657 : }
658 :
659 : /*
660 : * Function to return the list of files in the WAL archive status directory.
661 : */
662 : Datum
663 6 : pg_ls_archive_statusdir(PG_FUNCTION_ARGS)
664 : {
665 6 : return pg_ls_dir_files(fcinfo, XLOGDIR "/archive_status", true);
666 : }
667 :
668 : /*
669 : * Function to return the list of files in the pg_logical/snapshots directory.
670 : */
671 : Datum
672 2 : pg_ls_logicalsnapdir(PG_FUNCTION_ARGS)
673 : {
674 2 : return pg_ls_dir_files(fcinfo, "pg_logical/snapshots", false);
675 : }
676 :
677 : /*
678 : * Function to return the list of files in the pg_logical/mappings directory.
679 : */
680 : Datum
681 2 : pg_ls_logicalmapdir(PG_FUNCTION_ARGS)
682 : {
683 2 : return pg_ls_dir_files(fcinfo, "pg_logical/mappings", false);
684 : }
685 :
686 : /*
687 : * Function to return the list of files in the pg_replslot/<replication_slot>
688 : * directory.
689 : */
690 : Datum
691 4 : pg_ls_replslotdir(PG_FUNCTION_ARGS)
692 : {
693 : text *slotname_t;
694 : char path[MAXPGPATH];
695 : char *slotname;
696 :
697 4 : slotname_t = PG_GETARG_TEXT_PP(0);
698 :
699 4 : slotname = text_to_cstring(slotname_t);
700 :
701 4 : if (!SearchNamedReplicationSlot(slotname, true))
702 2 : ereport(ERROR,
703 : (errcode(ERRCODE_UNDEFINED_OBJECT),
704 : errmsg("replication slot \"%s\" does not exist",
705 : slotname)));
706 :
707 2 : snprintf(path, sizeof(path), "pg_replslot/%s", slotname);
708 2 : return pg_ls_dir_files(fcinfo, path, false);
709 : }
|