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

Generated by: LCOV version 1.13