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

Generated by: LCOV version 1.13