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