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