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