LCOV - code coverage report
Current view: top level - src/backend/utils/adt - genfile.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 0 195 0.0 %
Date: 2017-09-29 13:40:31 Functions: 0 16 0.0 %
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-2017, 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_type.h"
      26             : #include "funcapi.h"
      27             : #include "mb/pg_wchar.h"
      28             : #include "miscadmin.h"
      29             : #include "postmaster/syslogger.h"
      30             : #include "storage/fd.h"
      31             : #include "utils/builtins.h"
      32             : #include "utils/memutils.h"
      33             : #include "utils/timestamp.h"
      34             : 
      35             : typedef struct
      36             : {
      37             :     char       *location;
      38             :     DIR        *dirdesc;
      39             :     bool        include_dot_dirs;
      40             : } directory_fctx;
      41             : 
      42             : 
      43             : /*
      44             :  * Convert a "text" filename argument to C string, and check it's allowable.
      45             :  *
      46             :  * Filename may be absolute or relative to the DataDir, but we only allow
      47             :  * absolute paths that match DataDir or Log_directory.
      48             :  */
      49             : static char *
      50           0 : convert_and_check_filename(text *arg)
      51             : {
      52             :     char       *filename;
      53             : 
      54           0 :     filename = text_to_cstring(arg);
      55           0 :     canonicalize_path(filename);    /* filename can change length here */
      56             : 
      57           0 :     if (is_absolute_path(filename))
      58             :     {
      59             :         /* Disallow '/a/b/data/..' */
      60           0 :         if (path_contains_parent_reference(filename))
      61           0 :             ereport(ERROR,
      62             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      63             :                      (errmsg("reference to parent directory (\"..\") not allowed"))));
      64             : 
      65             :         /*
      66             :          * Allow absolute paths if within DataDir or Log_directory, even
      67             :          * though Log_directory might be outside DataDir.
      68             :          */
      69           0 :         if (!path_is_prefix_of_path(DataDir, filename) &&
      70           0 :             (!is_absolute_path(Log_directory) ||
      71           0 :              !path_is_prefix_of_path(Log_directory, filename)))
      72           0 :             ereport(ERROR,
      73             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      74             :                      (errmsg("absolute path not allowed"))));
      75             :     }
      76           0 :     else if (!path_is_relative_and_below_cwd(filename))
      77           0 :         ereport(ERROR,
      78             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      79             :                  (errmsg("path must be in or below the current directory"))));
      80             : 
      81           0 :     return filename;
      82             : }
      83             : 
      84             : 
      85             : /*
      86             :  * Read a section of a file, returning it as bytea
      87             :  *
      88             :  * Caller is responsible for all permissions checking.
      89             :  *
      90             :  * We read the whole of the file when bytes_to_read is negative.
      91             :  */
      92             : static bytea *
      93           0 : read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
      94             :                  bool missing_ok)
      95             : {
      96             :     bytea      *buf;
      97             :     size_t      nbytes;
      98             :     FILE       *file;
      99             : 
     100           0 :     if (bytes_to_read < 0)
     101             :     {
     102           0 :         if (seek_offset < 0)
     103           0 :             bytes_to_read = -seek_offset;
     104             :         else
     105             :         {
     106             :             struct stat fst;
     107             : 
     108           0 :             if (stat(filename, &fst) < 0)
     109             :             {
     110           0 :                 if (missing_ok && errno == ENOENT)
     111           0 :                     return NULL;
     112             :                 else
     113           0 :                     ereport(ERROR,
     114             :                             (errcode_for_file_access(),
     115             :                              errmsg("could not stat file \"%s\": %m", filename)));
     116             :             }
     117             : 
     118           0 :             bytes_to_read = fst.st_size - seek_offset;
     119             :         }
     120             :     }
     121             : 
     122             :     /* not sure why anyone thought that int64 length was a good idea */
     123           0 :     if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
     124           0 :         ereport(ERROR,
     125             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     126             :                  errmsg("requested length too large")));
     127             : 
     128           0 :     if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
     129             :     {
     130           0 :         if (missing_ok && errno == ENOENT)
     131           0 :             return NULL;
     132             :         else
     133           0 :             ereport(ERROR,
     134             :                     (errcode_for_file_access(),
     135             :                      errmsg("could not open file \"%s\" for reading: %m",
     136             :                             filename)));
     137             :     }
     138             : 
     139           0 :     if (fseeko(file, (off_t) seek_offset,
     140             :                (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
     141           0 :         ereport(ERROR,
     142             :                 (errcode_for_file_access(),
     143             :                  errmsg("could not seek in file \"%s\": %m", filename)));
     144             : 
     145           0 :     buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
     146             : 
     147           0 :     nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
     148             : 
     149           0 :     if (ferror(file))
     150           0 :         ereport(ERROR,
     151             :                 (errcode_for_file_access(),
     152             :                  errmsg("could not read file \"%s\": %m", filename)));
     153             : 
     154           0 :     SET_VARSIZE(buf, nbytes + VARHDRSZ);
     155             : 
     156           0 :     FreeFile(file);
     157             : 
     158           0 :     return buf;
     159             : }
     160             : 
     161             : /*
     162             :  * Similar to read_binary_file, but we verify that the contents are valid
     163             :  * in the database encoding.
     164             :  */
     165             : static text *
     166           0 : read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
     167             :                bool missing_ok)
     168             : {
     169             :     bytea      *buf;
     170             : 
     171           0 :     buf = read_binary_file(filename, seek_offset, bytes_to_read, missing_ok);
     172             : 
     173           0 :     if (buf != NULL)
     174             :     {
     175             :         /* Make sure the input is valid */
     176           0 :         pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
     177             : 
     178             :         /* OK, we can cast it to text safely */
     179           0 :         return (text *) buf;
     180             :     }
     181             :     else
     182           0 :         return NULL;
     183             : }
     184             : 
     185             : /*
     186             :  * Read a section of a file, returning it as text
     187             :  */
     188             : Datum
     189           0 : pg_read_file(PG_FUNCTION_ARGS)
     190             : {
     191           0 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     192           0 :     int64       seek_offset = 0;
     193           0 :     int64       bytes_to_read = -1;
     194           0 :     bool        missing_ok = false;
     195             :     char       *filename;
     196             :     text       *result;
     197             : 
     198           0 :     if (!superuser())
     199           0 :         ereport(ERROR,
     200             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     201             :                  (errmsg("must be superuser to read files"))));
     202             : 
     203             :     /* handle optional arguments */
     204           0 :     if (PG_NARGS() >= 3)
     205             :     {
     206           0 :         seek_offset = PG_GETARG_INT64(1);
     207           0 :         bytes_to_read = PG_GETARG_INT64(2);
     208             : 
     209           0 :         if (bytes_to_read < 0)
     210           0 :             ereport(ERROR,
     211             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     212             :                      errmsg("requested length cannot be negative")));
     213             :     }
     214           0 :     if (PG_NARGS() >= 4)
     215           0 :         missing_ok = PG_GETARG_BOOL(3);
     216             : 
     217           0 :     filename = convert_and_check_filename(filename_t);
     218             : 
     219           0 :     result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
     220           0 :     if (result)
     221           0 :         PG_RETURN_TEXT_P(result);
     222             :     else
     223           0 :         PG_RETURN_NULL();
     224             : }
     225             : 
     226             : /*
     227             :  * Read a section of a file, returning it as bytea
     228             :  */
     229             : Datum
     230           0 : pg_read_binary_file(PG_FUNCTION_ARGS)
     231             : {
     232           0 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     233           0 :     int64       seek_offset = 0;
     234           0 :     int64       bytes_to_read = -1;
     235           0 :     bool        missing_ok = false;
     236             :     char       *filename;
     237             :     bytea      *result;
     238             : 
     239           0 :     if (!superuser())
     240           0 :         ereport(ERROR,
     241             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     242             :                  (errmsg("must be superuser to read files"))));
     243             : 
     244             :     /* handle optional arguments */
     245           0 :     if (PG_NARGS() >= 3)
     246             :     {
     247           0 :         seek_offset = PG_GETARG_INT64(1);
     248           0 :         bytes_to_read = PG_GETARG_INT64(2);
     249             : 
     250           0 :         if (bytes_to_read < 0)
     251           0 :             ereport(ERROR,
     252             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     253             :                      errmsg("requested length cannot be negative")));
     254             :     }
     255           0 :     if (PG_NARGS() >= 4)
     256           0 :         missing_ok = PG_GETARG_BOOL(3);
     257             : 
     258           0 :     filename = convert_and_check_filename(filename_t);
     259             : 
     260           0 :     result = read_binary_file(filename, seek_offset,
     261             :                               bytes_to_read, missing_ok);
     262           0 :     if (result)
     263           0 :         PG_RETURN_BYTEA_P(result);
     264             :     else
     265           0 :         PG_RETURN_NULL();
     266             : }
     267             : 
     268             : 
     269             : /*
     270             :  * Wrapper functions for the 1 and 3 argument variants of pg_read_file()
     271             :  * and pg_binary_read_file().
     272             :  *
     273             :  * These are necessary to pass the sanity check in opr_sanity, which checks
     274             :  * that all built-in functions that share the implementing C function take
     275             :  * the same number of arguments.
     276             :  */
     277             : Datum
     278           0 : pg_read_file_off_len(PG_FUNCTION_ARGS)
     279             : {
     280           0 :     return pg_read_file(fcinfo);
     281             : }
     282             : 
     283             : Datum
     284           0 : pg_read_file_all(PG_FUNCTION_ARGS)
     285             : {
     286           0 :     return pg_read_file(fcinfo);
     287             : }
     288             : 
     289             : Datum
     290           0 : pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
     291             : {
     292           0 :     return pg_read_binary_file(fcinfo);
     293             : }
     294             : 
     295             : Datum
     296           0 : pg_read_binary_file_all(PG_FUNCTION_ARGS)
     297             : {
     298           0 :     return pg_read_binary_file(fcinfo);
     299             : }
     300             : 
     301             : /*
     302             :  * stat a file
     303             :  */
     304             : Datum
     305           0 : pg_stat_file(PG_FUNCTION_ARGS)
     306             : {
     307           0 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     308             :     char       *filename;
     309             :     struct stat fst;
     310             :     Datum       values[6];
     311             :     bool        isnull[6];
     312             :     HeapTuple   tuple;
     313             :     TupleDesc   tupdesc;
     314           0 :     bool        missing_ok = false;
     315             : 
     316           0 :     if (!superuser())
     317           0 :         ereport(ERROR,
     318             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     319             :                  (errmsg("must be superuser to get file information"))));
     320             : 
     321             :     /* check the optional argument */
     322           0 :     if (PG_NARGS() == 2)
     323           0 :         missing_ok = PG_GETARG_BOOL(1);
     324             : 
     325           0 :     filename = convert_and_check_filename(filename_t);
     326             : 
     327           0 :     if (stat(filename, &fst) < 0)
     328             :     {
     329           0 :         if (missing_ok && errno == ENOENT)
     330           0 :             PG_RETURN_NULL();
     331             :         else
     332           0 :             ereport(ERROR,
     333             :                     (errcode_for_file_access(),
     334             :                      errmsg("could not stat file \"%s\": %m", filename)));
     335             :     }
     336             : 
     337             :     /*
     338             :      * This record type had better match the output parameters declared for me
     339             :      * in pg_proc.h.
     340             :      */
     341           0 :     tupdesc = CreateTemplateTupleDesc(6, false);
     342           0 :     TupleDescInitEntry(tupdesc, (AttrNumber) 1,
     343             :                        "size", INT8OID, -1, 0);
     344           0 :     TupleDescInitEntry(tupdesc, (AttrNumber) 2,
     345             :                        "access", TIMESTAMPTZOID, -1, 0);
     346           0 :     TupleDescInitEntry(tupdesc, (AttrNumber) 3,
     347             :                        "modification", TIMESTAMPTZOID, -1, 0);
     348           0 :     TupleDescInitEntry(tupdesc, (AttrNumber) 4,
     349             :                        "change", TIMESTAMPTZOID, -1, 0);
     350           0 :     TupleDescInitEntry(tupdesc, (AttrNumber) 5,
     351             :                        "creation", TIMESTAMPTZOID, -1, 0);
     352           0 :     TupleDescInitEntry(tupdesc, (AttrNumber) 6,
     353             :                        "isdir", BOOLOID, -1, 0);
     354           0 :     BlessTupleDesc(tupdesc);
     355             : 
     356           0 :     memset(isnull, false, sizeof(isnull));
     357             : 
     358           0 :     values[0] = Int64GetDatum((int64) fst.st_size);
     359           0 :     values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
     360           0 :     values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
     361             :     /* Unix has file status change time, while Win32 has creation time */
     362             : #if !defined(WIN32) && !defined(__CYGWIN__)
     363           0 :     values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
     364           0 :     isnull[4] = true;
     365             : #else
     366             :     isnull[3] = true;
     367             :     values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
     368             : #endif
     369           0 :     values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
     370             : 
     371           0 :     tuple = heap_form_tuple(tupdesc, values, isnull);
     372             : 
     373           0 :     pfree(filename);
     374             : 
     375           0 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     376             : }
     377             : 
     378             : /*
     379             :  * stat a file (1 argument version)
     380             :  *
     381             :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
     382             :  * which checks that all built-in functions that share the implementing C
     383             :  * function take the same number of arguments
     384             :  */
     385             : Datum
     386           0 : pg_stat_file_1arg(PG_FUNCTION_ARGS)
     387             : {
     388           0 :     return pg_stat_file(fcinfo);
     389             : }
     390             : 
     391             : /*
     392             :  * List a directory (returns the filenames only)
     393             :  */
     394             : Datum
     395           0 : pg_ls_dir(PG_FUNCTION_ARGS)
     396             : {
     397             :     FuncCallContext *funcctx;
     398             :     struct dirent *de;
     399             :     directory_fctx *fctx;
     400             :     MemoryContext oldcontext;
     401             : 
     402           0 :     if (!superuser())
     403           0 :         ereport(ERROR,
     404             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     405             :                  (errmsg("must be superuser to get directory listings"))));
     406             : 
     407           0 :     if (SRF_IS_FIRSTCALL())
     408             :     {
     409           0 :         bool        missing_ok = false;
     410           0 :         bool        include_dot_dirs = false;
     411             : 
     412             :         /* check the optional arguments */
     413           0 :         if (PG_NARGS() == 3)
     414             :         {
     415           0 :             if (!PG_ARGISNULL(1))
     416           0 :                 missing_ok = PG_GETARG_BOOL(1);
     417           0 :             if (!PG_ARGISNULL(2))
     418           0 :                 include_dot_dirs = PG_GETARG_BOOL(2);
     419             :         }
     420             : 
     421           0 :         funcctx = SRF_FIRSTCALL_INIT();
     422           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     423             : 
     424           0 :         fctx = palloc(sizeof(directory_fctx));
     425           0 :         fctx->location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
     426             : 
     427           0 :         fctx->include_dot_dirs = include_dot_dirs;
     428           0 :         fctx->dirdesc = AllocateDir(fctx->location);
     429             : 
     430           0 :         if (!fctx->dirdesc)
     431             :         {
     432           0 :             if (missing_ok && errno == ENOENT)
     433             :             {
     434           0 :                 MemoryContextSwitchTo(oldcontext);
     435           0 :                 SRF_RETURN_DONE(funcctx);
     436             :             }
     437             :             else
     438           0 :                 ereport(ERROR,
     439             :                         (errcode_for_file_access(),
     440             :                          errmsg("could not open directory \"%s\": %m",
     441             :                                 fctx->location)));
     442             :         }
     443           0 :         funcctx->user_fctx = fctx;
     444           0 :         MemoryContextSwitchTo(oldcontext);
     445             :     }
     446             : 
     447           0 :     funcctx = SRF_PERCALL_SETUP();
     448           0 :     fctx = (directory_fctx *) funcctx->user_fctx;
     449             : 
     450           0 :     while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
     451             :     {
     452           0 :         if (!fctx->include_dot_dirs &&
     453           0 :             (strcmp(de->d_name, ".") == 0 ||
     454           0 :              strcmp(de->d_name, "..") == 0))
     455           0 :             continue;
     456             : 
     457           0 :         SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
     458             :     }
     459             : 
     460           0 :     FreeDir(fctx->dirdesc);
     461             : 
     462           0 :     SRF_RETURN_DONE(funcctx);
     463             : }
     464             : 
     465             : /*
     466             :  * List a directory (1 argument version)
     467             :  *
     468             :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
     469             :  * which checks that all built-in functions that share the implementing C
     470             :  * function take the same number of arguments.
     471             :  */
     472             : Datum
     473           0 : pg_ls_dir_1arg(PG_FUNCTION_ARGS)
     474             : {
     475           0 :     return pg_ls_dir(fcinfo);
     476             : }
     477             : 
     478             : /* Generic function to return a directory listing of files */
     479             : static Datum
     480           0 : pg_ls_dir_files(FunctionCallInfo fcinfo, char *dir)
     481             : {
     482             :     FuncCallContext *funcctx;
     483             :     struct dirent *de;
     484             :     directory_fctx *fctx;
     485             : 
     486           0 :     if (SRF_IS_FIRSTCALL())
     487             :     {
     488             :         MemoryContext oldcontext;
     489             :         TupleDesc   tupdesc;
     490             : 
     491           0 :         funcctx = SRF_FIRSTCALL_INIT();
     492           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     493             : 
     494           0 :         fctx = palloc(sizeof(directory_fctx));
     495             : 
     496           0 :         tupdesc = CreateTemplateTupleDesc(3, false);
     497           0 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
     498             :                            TEXTOID, -1, 0);
     499           0 :         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size",
     500             :                            INT8OID, -1, 0);
     501           0 :         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "modification",
     502             :                            TIMESTAMPTZOID, -1, 0);
     503           0 :         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
     504             : 
     505           0 :         fctx->location = pstrdup(dir);
     506           0 :         fctx->dirdesc = AllocateDir(fctx->location);
     507             : 
     508           0 :         if (!fctx->dirdesc)
     509           0 :             ereport(ERROR,
     510             :                     (errcode_for_file_access(),
     511             :                      errmsg("could not read directory \"%s\": %m",
     512             :                             fctx->location)));
     513             : 
     514           0 :         funcctx->user_fctx = fctx;
     515           0 :         MemoryContextSwitchTo(oldcontext);
     516             :     }
     517             : 
     518           0 :     funcctx = SRF_PERCALL_SETUP();
     519           0 :     fctx = (directory_fctx *) funcctx->user_fctx;
     520             : 
     521           0 :     while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
     522             :     {
     523             :         Datum       values[3];
     524             :         bool        nulls[3];
     525             :         char        path[MAXPGPATH * 2];
     526             :         struct stat attrib;
     527             :         HeapTuple   tuple;
     528             : 
     529             :         /* Skip hidden files */
     530           0 :         if (de->d_name[0] == '.')
     531           0 :             continue;
     532             : 
     533             :         /* Get the file info */
     534           0 :         snprintf(path, sizeof(path), "%s/%s", fctx->location, de->d_name);
     535           0 :         if (stat(path, &attrib) < 0)
     536           0 :             ereport(ERROR,
     537             :                     (errcode_for_file_access(),
     538             :                      errmsg("could not stat directory \"%s\": %m", dir)));
     539             : 
     540             :         /* Ignore anything but regular files */
     541           0 :         if (!S_ISREG(attrib.st_mode))
     542           0 :             continue;
     543             : 
     544           0 :         values[0] = CStringGetTextDatum(de->d_name);
     545           0 :         values[1] = Int64GetDatum((int64) attrib.st_size);
     546           0 :         values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
     547           0 :         memset(nulls, 0, sizeof(nulls));
     548             : 
     549           0 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     550           0 :         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
     551             :     }
     552             : 
     553           0 :     FreeDir(fctx->dirdesc);
     554           0 :     SRF_RETURN_DONE(funcctx);
     555             : }
     556             : 
     557             : /* Function to return the list of files in the log directory */
     558             : Datum
     559           0 : pg_ls_logdir(PG_FUNCTION_ARGS)
     560             : {
     561           0 :     return pg_ls_dir_files(fcinfo, Log_directory);
     562             : }
     563             : 
     564             : /* Function to return the list of files in the WAL directory */
     565             : Datum
     566           0 : pg_ls_waldir(PG_FUNCTION_ARGS)
     567             : {
     568           0 :     return pg_ls_dir_files(fcinfo, XLOGDIR);
     569             : }

Generated by: LCOV version 1.11