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

Generated by: LCOV version 2.0-1