LCOV - code coverage report
Current view: top level - src/backend/utils/adt - genfile.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 83.8 % 229 192
Test Date: 2026-03-25 01:16:12 Functions: 85.7 % 28 24
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * genfile.c
       4              :  *      Functions for direct access to files
       5              :  *
       6              :  *
       7              :  * Copyright (c) 2004-2026, 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        10028 : convert_and_check_filename(text *arg)
      55              : {
      56              :     char       *filename;
      57              : 
      58        10028 :     filename = text_to_cstring(arg);
      59        10028 :     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        10028 :     if (has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
      67         2857 :         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         7171 :     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         7171 :     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 data directory")));
      90              : 
      91         7171 :     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         2415 : read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
     104              :                  bool missing_ok)
     105              : {
     106              :     bytea      *buf;
     107         2415 :     size_t      nbytes = 0;
     108              :     FILE       *file;
     109              : 
     110              :     /* clamp request size to what we can actually deliver */
     111         2415 :     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         2415 :     if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
     117              :     {
     118           16 :         if (missing_ok && errno == ENOENT)
     119            8 :             return NULL;
     120              :         else
     121            8 :             ereport(ERROR,
     122              :                     (errcode_for_file_access(),
     123              :                      errmsg("could not open file \"%s\" for reading: %m",
     124              :                             filename)));
     125              :     }
     126              : 
     127         2399 :     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         2399 :     if (bytes_to_read >= 0)
     134              :     {
     135              :         /* If passed explicit read size just do it */
     136         2374 :         buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
     137              : 
     138         2374 :         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           25 :         initStringInfo(&sbuf);
     146              :         /* Leave room in the buffer for the varlena length word */
     147           25 :         sbuf.len += VARHDRSZ;
     148              :         Assert(sbuf.len < sbuf.maxlen);
     149              : 
     150           62 :         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           37 :             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           37 :             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           37 :             rbytes = fread(sbuf.data + sbuf.len, 1,
     185           37 :                            (size_t) (sbuf.maxlen - sbuf.len - 1), file);
     186           37 :             sbuf.len += rbytes;
     187           37 :             nbytes += rbytes;
     188              :         }
     189              : 
     190              :         /* Now we can commandeer the stringinfo's buffer as the result */
     191           25 :         buf = (bytea *) sbuf.data;
     192              :     }
     193              : 
     194         2399 :     if (ferror(file))
     195            0 :         ereport(ERROR,
     196              :                 (errcode_for_file_access(),
     197              :                  errmsg("could not read file \"%s\": %m", filename)));
     198              : 
     199         2399 :     SET_VARSIZE(buf, nbytes + VARHDRSZ);
     200              : 
     201         2399 :     FreeFile(file);
     202              : 
     203         2399 :     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            8 :         pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
     222              : 
     223              :         /* OK, we can cast it to text safely */
     224            8 :         return (text *) buf;
     225              :     }
     226              :     else
     227            4 :         return NULL;
     228              : }
     229              : 
     230              : /*
     231              :  * Read a section of a file, returning it as text
     232              :  *
     233              :  * No superuser check done here- instead privileges are handled by the
     234              :  * GRANT system.
     235              :  *
     236              :  * If read_to_eof is true, bytes_to_read must be -1, otherwise negative values
     237              :  * are not allowed for bytes_to_read.
     238              :  */
     239              : static text *
     240           24 : pg_read_file_common(text *filename_t, int64 seek_offset, int64 bytes_to_read,
     241              :                     bool read_to_eof, bool missing_ok)
     242              : {
     243           24 :     if (read_to_eof)
     244              :         Assert(bytes_to_read == -1);
     245           12 :     else if (bytes_to_read < 0)
     246            8 :         ereport(ERROR,
     247              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     248              :                  errmsg("requested length cannot be negative")));
     249              : 
     250           16 :     return read_text_file(convert_and_check_filename(filename_t),
     251              :                           seek_offset, bytes_to_read, missing_ok);
     252              : }
     253              : 
     254              : /*
     255              :  * Read a section of a file, returning it as bytea
     256              :  *
     257              :  * Parameters are interpreted the same as pg_read_file_common().
     258              :  */
     259              : static bytea *
     260         2407 : pg_read_binary_file_common(text *filename_t,
     261              :                            int64 seek_offset, int64 bytes_to_read,
     262              :                            bool read_to_eof, bool missing_ok)
     263              : {
     264         2407 :     if (read_to_eof)
     265              :         Assert(bytes_to_read == -1);
     266         2378 :     else if (bytes_to_read < 0)
     267            8 :         ereport(ERROR,
     268              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     269              :                  errmsg("requested length cannot be negative")));
     270              : 
     271         2399 :     return read_binary_file(convert_and_check_filename(filename_t),
     272              :                             seek_offset, bytes_to_read, missing_ok);
     273              : }
     274              : 
     275              : 
     276              : /*
     277              :  * Wrapper functions for the variants of SQL functions pg_read_file() and
     278              :  * pg_read_binary_file().
     279              :  *
     280              :  * These are necessary to pass the sanity check in opr_sanity, which checks
     281              :  * that all built-in functions that share the implementing C function take
     282              :  * the same number of arguments.
     283              :  */
     284              : Datum
     285            8 : pg_read_file_off_len(PG_FUNCTION_ARGS)
     286              : {
     287            8 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     288            8 :     int64       seek_offset = PG_GETARG_INT64(1);
     289            8 :     int64       bytes_to_read = PG_GETARG_INT64(2);
     290              :     text       *ret;
     291              : 
     292            8 :     ret = pg_read_file_common(filename_t, seek_offset, bytes_to_read,
     293              :                               false, false);
     294            4 :     if (!ret)
     295            0 :         PG_RETURN_NULL();
     296              : 
     297            4 :     PG_RETURN_TEXT_P(ret);
     298              : }
     299              : 
     300              : Datum
     301            4 : pg_read_file_off_len_missing(PG_FUNCTION_ARGS)
     302              : {
     303            4 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     304            4 :     int64       seek_offset = PG_GETARG_INT64(1);
     305            4 :     int64       bytes_to_read = PG_GETARG_INT64(2);
     306            4 :     bool        missing_ok = PG_GETARG_BOOL(3);
     307              :     text       *ret;
     308              : 
     309            4 :     ret = pg_read_file_common(filename_t, seek_offset, bytes_to_read,
     310              :                               false, missing_ok);
     311              : 
     312            0 :     if (!ret)
     313            0 :         PG_RETURN_NULL();
     314              : 
     315            0 :     PG_RETURN_TEXT_P(ret);
     316              : }
     317              : 
     318              : Datum
     319            8 : pg_read_file_all(PG_FUNCTION_ARGS)
     320              : {
     321            8 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     322              :     text       *ret;
     323              : 
     324            8 :     ret = pg_read_file_common(filename_t, 0, -1, true, false);
     325              : 
     326            4 :     if (!ret)
     327            0 :         PG_RETURN_NULL();
     328              : 
     329            4 :     PG_RETURN_TEXT_P(ret);
     330              : }
     331              : 
     332              : Datum
     333            4 : pg_read_file_all_missing(PG_FUNCTION_ARGS)
     334              : {
     335            4 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     336            4 :     bool        missing_ok = PG_GETARG_BOOL(1);
     337              :     text       *ret;
     338              : 
     339            4 :     ret = pg_read_file_common(filename_t, 0, -1, true, missing_ok);
     340              : 
     341            4 :     if (!ret)
     342            4 :         PG_RETURN_NULL();
     343              : 
     344            0 :     PG_RETURN_TEXT_P(ret);
     345              : }
     346              : 
     347              : Datum
     348            8 : pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
     349              : {
     350            8 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     351            8 :     int64       seek_offset = PG_GETARG_INT64(1);
     352            8 :     int64       bytes_to_read = PG_GETARG_INT64(2);
     353              :     text       *ret;
     354              : 
     355            8 :     ret = pg_read_binary_file_common(filename_t, seek_offset, bytes_to_read,
     356              :                                      false, false);
     357            4 :     if (!ret)
     358            0 :         PG_RETURN_NULL();
     359              : 
     360            4 :     PG_RETURN_BYTEA_P(ret);
     361              : }
     362              : 
     363              : Datum
     364         2370 : pg_read_binary_file_off_len_missing(PG_FUNCTION_ARGS)
     365              : {
     366         2370 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     367         2370 :     int64       seek_offset = PG_GETARG_INT64(1);
     368         2370 :     int64       bytes_to_read = PG_GETARG_INT64(2);
     369         2370 :     bool        missing_ok = PG_GETARG_BOOL(3);
     370              :     text       *ret;
     371              : 
     372         2370 :     ret = pg_read_binary_file_common(filename_t, seek_offset, bytes_to_read,
     373              :                                      false, missing_ok);
     374         2366 :     if (!ret)
     375            0 :         PG_RETURN_NULL();
     376              : 
     377         2366 :     PG_RETURN_BYTEA_P(ret);
     378              : }
     379              : 
     380              : Datum
     381           25 : pg_read_binary_file_all(PG_FUNCTION_ARGS)
     382              : {
     383           25 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     384              :     text       *ret;
     385              : 
     386           25 :     ret = pg_read_binary_file_common(filename_t, 0, -1, true, false);
     387              : 
     388           21 :     if (!ret)
     389            0 :         PG_RETURN_NULL();
     390              : 
     391           21 :     PG_RETURN_BYTEA_P(ret);
     392              : }
     393              : 
     394              : Datum
     395            4 : pg_read_binary_file_all_missing(PG_FUNCTION_ARGS)
     396              : {
     397            4 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     398            4 :     bool        missing_ok = PG_GETARG_BOOL(1);
     399              :     text       *ret;
     400              : 
     401            4 :     ret = pg_read_binary_file_common(filename_t, 0, -1, true, missing_ok);
     402              : 
     403            4 :     if (!ret)
     404            4 :         PG_RETURN_NULL();
     405              : 
     406            0 :     PG_RETURN_BYTEA_P(ret);
     407              : }
     408              : 
     409              : /*
     410              :  * stat a file
     411              :  */
     412              : Datum
     413         7410 : pg_stat_file(PG_FUNCTION_ARGS)
     414              : {
     415         7410 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     416              :     char       *filename;
     417              :     struct stat fst;
     418              :     Datum       values[6];
     419              :     bool        isnull[6];
     420              :     HeapTuple   tuple;
     421              :     TupleDesc   tupdesc;
     422         7410 :     bool        missing_ok = false;
     423              : 
     424              :     /* check the optional argument */
     425         7410 :     if (PG_NARGS() == 2)
     426         7404 :         missing_ok = PG_GETARG_BOOL(1);
     427              : 
     428         7410 :     filename = convert_and_check_filename(filename_t);
     429              : 
     430         7410 :     if (stat(filename, &fst) < 0)
     431              :     {
     432            1 :         if (missing_ok && errno == ENOENT)
     433            1 :             PG_RETURN_NULL();
     434              :         else
     435            0 :             ereport(ERROR,
     436              :                     (errcode_for_file_access(),
     437              :                      errmsg("could not stat file \"%s\": %m", filename)));
     438              :     }
     439              : 
     440              :     /*
     441              :      * This record type had better match the output parameters declared for me
     442              :      * in pg_proc.h.
     443              :      */
     444         7409 :     tupdesc = CreateTemplateTupleDesc(6);
     445         7409 :     TupleDescInitEntry(tupdesc, (AttrNumber) 1,
     446              :                        "size", INT8OID, -1, 0);
     447         7409 :     TupleDescInitEntry(tupdesc, (AttrNumber) 2,
     448              :                        "access", TIMESTAMPTZOID, -1, 0);
     449         7409 :     TupleDescInitEntry(tupdesc, (AttrNumber) 3,
     450              :                        "modification", TIMESTAMPTZOID, -1, 0);
     451         7409 :     TupleDescInitEntry(tupdesc, (AttrNumber) 4,
     452              :                        "change", TIMESTAMPTZOID, -1, 0);
     453         7409 :     TupleDescInitEntry(tupdesc, (AttrNumber) 5,
     454              :                        "creation", TIMESTAMPTZOID, -1, 0);
     455         7409 :     TupleDescInitEntry(tupdesc, (AttrNumber) 6,
     456              :                        "isdir", BOOLOID, -1, 0);
     457         7409 :     TupleDescFinalize(tupdesc);
     458         7409 :     BlessTupleDesc(tupdesc);
     459              : 
     460         7409 :     memset(isnull, false, sizeof(isnull));
     461              : 
     462         7409 :     values[0] = Int64GetDatum((int64) fst.st_size);
     463         7409 :     values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
     464         7409 :     values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
     465              :     /* Unix has file status change time, while Win32 has creation time */
     466              : #if !defined(WIN32) && !defined(__CYGWIN__)
     467         7409 :     values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
     468         7409 :     isnull[4] = true;
     469              : #else
     470              :     isnull[3] = true;
     471              :     values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
     472              : #endif
     473         7409 :     values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
     474              : 
     475         7409 :     tuple = heap_form_tuple(tupdesc, values, isnull);
     476              : 
     477         7409 :     pfree(filename);
     478              : 
     479         7409 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     480              : }
     481              : 
     482              : /*
     483              :  * stat a file (1 argument version)
     484              :  *
     485              :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
     486              :  * which checks that all built-in functions that share the implementing C
     487              :  * function take the same number of arguments
     488              :  */
     489              : Datum
     490            6 : pg_stat_file_1arg(PG_FUNCTION_ARGS)
     491              : {
     492            6 :     return pg_stat_file(fcinfo);
     493              : }
     494              : 
     495              : /*
     496              :  * List a directory (returns the filenames only)
     497              :  */
     498              : Datum
     499          203 : pg_ls_dir(PG_FUNCTION_ARGS)
     500              : {
     501          203 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     502              :     char       *location;
     503          203 :     bool        missing_ok = false;
     504          203 :     bool        include_dot_dirs = false;
     505              :     DIR        *dirdesc;
     506              :     struct dirent *de;
     507              : 
     508          203 :     location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
     509              : 
     510              :     /* check the optional arguments */
     511          203 :     if (PG_NARGS() == 3)
     512              :     {
     513          193 :         if (!PG_ARGISNULL(1))
     514          193 :             missing_ok = PG_GETARG_BOOL(1);
     515          193 :         if (!PG_ARGISNULL(2))
     516          193 :             include_dot_dirs = PG_GETARG_BOOL(2);
     517              :     }
     518              : 
     519          203 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
     520              : 
     521          203 :     dirdesc = AllocateDir(location);
     522          203 :     if (!dirdesc)
     523              :     {
     524              :         /* Return empty tuplestore if appropriate */
     525            8 :         if (missing_ok && errno == ENOENT)
     526            4 :             return (Datum) 0;
     527              :         /* Otherwise, we can let ReadDir() throw the error */
     528              :     }
     529              : 
     530         8378 :     while ((de = ReadDir(dirdesc, location)) != NULL)
     531              :     {
     532              :         Datum       values[1];
     533              :         bool        nulls[1];
     534              : 
     535         8179 :         if (!include_dot_dirs &&
     536         8071 :             (strcmp(de->d_name, ".") == 0 ||
     537         7880 :              strcmp(de->d_name, "..") == 0))
     538          382 :             continue;
     539              : 
     540         7797 :         values[0] = CStringGetTextDatum(de->d_name);
     541         7797 :         nulls[0] = false;
     542              : 
     543         7797 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     544              :                              values, nulls);
     545              :     }
     546              : 
     547          195 :     FreeDir(dirdesc);
     548          195 :     return (Datum) 0;
     549              : }
     550              : 
     551              : /*
     552              :  * List a directory (1 argument version)
     553              :  *
     554              :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
     555              :  * which checks that all built-in functions that share the implementing C
     556              :  * function take the same number of arguments.
     557              :  */
     558              : Datum
     559           10 : pg_ls_dir_1arg(PG_FUNCTION_ARGS)
     560              : {
     561           10 :     return pg_ls_dir(fcinfo);
     562              : }
     563              : 
     564              : /*
     565              :  * Generic function to return a directory listing of files.
     566              :  *
     567              :  * If the directory isn't there, silently return an empty set if missing_ok.
     568              :  * Other unreadable-directory cases throw an error.
     569              :  */
     570              : static Datum
     571           29 : pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
     572              : {
     573           29 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     574              :     DIR        *dirdesc;
     575              :     struct dirent *de;
     576              : 
     577           29 :     InitMaterializedSRF(fcinfo, 0);
     578              : 
     579              :     /*
     580              :      * Now walk the directory.  Note that we must do this within a single SRF
     581              :      * call, not leave the directory open across multiple calls, since we
     582              :      * can't count on the SRF being run to completion.
     583              :      */
     584           29 :     dirdesc = AllocateDir(dir);
     585           29 :     if (!dirdesc)
     586              :     {
     587              :         /* Return empty tuplestore if appropriate */
     588            0 :         if (missing_ok && errno == ENOENT)
     589            0 :             return (Datum) 0;
     590              :         /* Otherwise, we can let ReadDir() throw the error */
     591              :     }
     592              : 
     593          464 :     while ((de = ReadDir(dirdesc, dir)) != NULL)
     594              :     {
     595              :         Datum       values[3];
     596              :         bool        nulls[3];
     597              :         char        path[MAXPGPATH * 2];
     598              :         struct stat attrib;
     599              : 
     600              :         /* Skip hidden files */
     601          435 :         if (de->d_name[0] == '.')
     602           90 :             continue;
     603              : 
     604              :         /* Get the file info */
     605          377 :         snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
     606          377 :         if (stat(path, &attrib) < 0)
     607              :         {
     608              :             /* Ignore concurrently-deleted files, else complain */
     609            0 :             if (errno == ENOENT)
     610            0 :                 continue;
     611            0 :             ereport(ERROR,
     612              :                     (errcode_for_file_access(),
     613              :                      errmsg("could not stat file \"%s\": %m", path)));
     614              :         }
     615              : 
     616              :         /* Ignore anything but regular files */
     617          377 :         if (!S_ISREG(attrib.st_mode))
     618           32 :             continue;
     619              : 
     620          345 :         values[0] = CStringGetTextDatum(de->d_name);
     621          345 :         values[1] = Int64GetDatum((int64) attrib.st_size);
     622          345 :         values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
     623          345 :         memset(nulls, 0, sizeof(nulls));
     624              : 
     625          345 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
     626              :     }
     627              : 
     628           29 :     FreeDir(dirdesc);
     629           29 :     return (Datum) 0;
     630              : }
     631              : 
     632              : /* Function to return the list of files in the log directory */
     633              : Datum
     634            0 : pg_ls_logdir(PG_FUNCTION_ARGS)
     635              : {
     636            0 :     return pg_ls_dir_files(fcinfo, Log_directory, false);
     637              : }
     638              : 
     639              : /* Function to return the list of files in the WAL directory */
     640              : Datum
     641           16 : pg_ls_waldir(PG_FUNCTION_ARGS)
     642              : {
     643           16 :     return pg_ls_dir_files(fcinfo, XLOGDIR, false);
     644              : }
     645              : 
     646              : /*
     647              :  * Generic function to return the list of files in pgsql_tmp
     648              :  */
     649              : static Datum
     650            0 : pg_ls_tmpdir(FunctionCallInfo fcinfo, Oid tblspc)
     651              : {
     652              :     char        path[MAXPGPATH];
     653              : 
     654            0 :     if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tblspc)))
     655            0 :         ereport(ERROR,
     656              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     657              :                  errmsg("tablespace with OID %u does not exist",
     658              :                         tblspc)));
     659              : 
     660            0 :     TempTablespacePath(path, tblspc);
     661            0 :     return pg_ls_dir_files(fcinfo, path, true);
     662              : }
     663              : 
     664              : /*
     665              :  * Function to return the list of temporary files in the pg_default tablespace's
     666              :  * pgsql_tmp directory
     667              :  */
     668              : Datum
     669            0 : pg_ls_tmpdir_noargs(PG_FUNCTION_ARGS)
     670              : {
     671            0 :     return pg_ls_tmpdir(fcinfo, DEFAULTTABLESPACE_OID);
     672              : }
     673              : 
     674              : /*
     675              :  * Function to return the list of temporary files in the specified tablespace's
     676              :  * pgsql_tmp directory
     677              :  */
     678              : Datum
     679            0 : pg_ls_tmpdir_1arg(PG_FUNCTION_ARGS)
     680              : {
     681            0 :     return pg_ls_tmpdir(fcinfo, PG_GETARG_OID(0));
     682              : }
     683              : 
     684              : /*
     685              :  * Function to return the list of files in the WAL archive status directory.
     686              :  */
     687              : Datum
     688            4 : pg_ls_archive_statusdir(PG_FUNCTION_ARGS)
     689              : {
     690            4 :     return pg_ls_dir_files(fcinfo, XLOGDIR "/archive_status", true);
     691              : }
     692              : 
     693              : /*
     694              :  * Function to return the list of files in the WAL summaries directory.
     695              :  */
     696              : Datum
     697            4 : pg_ls_summariesdir(PG_FUNCTION_ARGS)
     698              : {
     699            4 :     return pg_ls_dir_files(fcinfo, XLOGDIR "/summaries", true);
     700              : }
     701              : 
     702              : /*
     703              :  * Function to return the list of files in the PG_LOGICAL_SNAPSHOTS_DIR
     704              :  * directory.
     705              :  */
     706              : Datum
     707            3 : pg_ls_logicalsnapdir(PG_FUNCTION_ARGS)
     708              : {
     709            3 :     return pg_ls_dir_files(fcinfo, PG_LOGICAL_SNAPSHOTS_DIR, false);
     710              : }
     711              : 
     712              : /*
     713              :  * Function to return the list of files in the PG_LOGICAL_MAPPINGS_DIR
     714              :  * directory.
     715              :  */
     716              : Datum
     717            1 : pg_ls_logicalmapdir(PG_FUNCTION_ARGS)
     718              : {
     719            1 :     return pg_ls_dir_files(fcinfo, PG_LOGICAL_MAPPINGS_DIR, false);
     720              : }
     721              : 
     722              : /*
     723              :  * Function to return the list of files in the PG_REPLSLOT_DIR/<slot_name>
     724              :  * directory.
     725              :  */
     726              : Datum
     727            2 : pg_ls_replslotdir(PG_FUNCTION_ARGS)
     728              : {
     729              :     text       *slotname_t;
     730              :     char        path[MAXPGPATH];
     731              :     char       *slotname;
     732              : 
     733            2 :     slotname_t = PG_GETARG_TEXT_PP(0);
     734              : 
     735            2 :     slotname = text_to_cstring(slotname_t);
     736              : 
     737            2 :     if (!SearchNamedReplicationSlot(slotname, true))
     738            1 :         ereport(ERROR,
     739              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     740              :                  errmsg("replication slot \"%s\" does not exist",
     741              :                         slotname)));
     742              : 
     743            1 :     snprintf(path, sizeof(path), "%s/%s", PG_REPLSLOT_DIR, slotname);
     744              : 
     745            1 :     return pg_ls_dir_files(fcinfo, path, false);
     746              : }
        

Generated by: LCOV version 2.0-1