Header And Logo

PostgreSQL
| The world's most advanced open source database.

genfile.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * genfile.c
00004  *      Functions for direct access to files
00005  *
00006  *
00007  * Copyright (c) 2004-2013, PostgreSQL Global Development Group
00008  *
00009  * Author: Andreas Pflug <[email protected]>
00010  *
00011  * IDENTIFICATION
00012  *    src/backend/utils/adt/genfile.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 #include "postgres.h"
00017 
00018 #include <sys/file.h>
00019 #include <sys/stat.h>
00020 #include <unistd.h>
00021 #include <dirent.h>
00022 
00023 #include "access/htup_details.h"
00024 #include "catalog/pg_type.h"
00025 #include "funcapi.h"
00026 #include "mb/pg_wchar.h"
00027 #include "miscadmin.h"
00028 #include "postmaster/syslogger.h"
00029 #include "storage/fd.h"
00030 #include "utils/builtins.h"
00031 #include "utils/memutils.h"
00032 #include "utils/timestamp.h"
00033 
00034 typedef struct
00035 {
00036     char       *location;
00037     DIR        *dirdesc;
00038 } directory_fctx;
00039 
00040 
00041 /*
00042  * Convert a "text" filename argument to C string, and check it's allowable.
00043  *
00044  * Filename may be absolute or relative to the DataDir, but we only allow
00045  * absolute paths that match DataDir or Log_directory.
00046  */
00047 static char *
00048 convert_and_check_filename(text *arg)
00049 {
00050     char       *filename;
00051 
00052     filename = text_to_cstring(arg);
00053     canonicalize_path(filename);    /* filename can change length here */
00054 
00055     if (is_absolute_path(filename))
00056     {
00057         /* Disallow '/a/b/data/..' */
00058         if (path_contains_parent_reference(filename))
00059             ereport(ERROR,
00060                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00061             (errmsg("reference to parent directory (\"..\") not allowed"))));
00062 
00063         /*
00064          * Allow absolute paths if within DataDir or Log_directory, even
00065          * though Log_directory might be outside DataDir.
00066          */
00067         if (!path_is_prefix_of_path(DataDir, filename) &&
00068             (!is_absolute_path(Log_directory) ||
00069              !path_is_prefix_of_path(Log_directory, filename)))
00070             ereport(ERROR,
00071                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00072                      (errmsg("absolute path not allowed"))));
00073     }
00074     else if (!path_is_relative_and_below_cwd(filename))
00075         ereport(ERROR,
00076                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00077                  (errmsg("path must be in or below the current directory"))));
00078 
00079     return filename;
00080 }
00081 
00082 
00083 /*
00084  * Read a section of a file, returning it as bytea
00085  *
00086  * Caller is responsible for all permissions checking.
00087  *
00088  * We read the whole of the file when bytes_to_read is negative.
00089  */
00090 bytea *
00091 read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
00092 {
00093     bytea      *buf;
00094     size_t      nbytes;
00095     FILE       *file;
00096 
00097     if (bytes_to_read < 0)
00098     {
00099         if (seek_offset < 0)
00100             bytes_to_read = -seek_offset;
00101         else
00102         {
00103             struct stat fst;
00104 
00105             if (stat(filename, &fst) < 0)
00106                 ereport(ERROR,
00107                         (errcode_for_file_access(),
00108                          errmsg("could not stat file \"%s\": %m", filename)));
00109 
00110             bytes_to_read = fst.st_size - seek_offset;
00111         }
00112     }
00113 
00114     /* not sure why anyone thought that int64 length was a good idea */
00115     if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
00116         ereport(ERROR,
00117                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00118                  errmsg("requested length too large")));
00119 
00120     if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
00121         ereport(ERROR,
00122                 (errcode_for_file_access(),
00123                  errmsg("could not open file \"%s\" for reading: %m",
00124                         filename)));
00125 
00126     if (fseeko(file, (off_t) seek_offset,
00127                (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
00128         ereport(ERROR,
00129                 (errcode_for_file_access(),
00130                  errmsg("could not seek in file \"%s\": %m", filename)));
00131 
00132     buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
00133 
00134     nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
00135 
00136     if (ferror(file))
00137         ereport(ERROR,
00138                 (errcode_for_file_access(),
00139                  errmsg("could not read file \"%s\": %m", filename)));
00140 
00141     SET_VARSIZE(buf, nbytes + VARHDRSZ);
00142 
00143     FreeFile(file);
00144 
00145     return buf;
00146 }
00147 
00148 /*
00149  * Similar to read_binary_file, but we verify that the contents are valid
00150  * in the database encoding.
00151  */
00152 static text *
00153 read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
00154 {
00155     bytea      *buf;
00156 
00157     buf = read_binary_file(filename, seek_offset, bytes_to_read);
00158 
00159     /* Make sure the input is valid */
00160     pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
00161 
00162     /* OK, we can cast it to text safely */
00163     return (text *) buf;
00164 }
00165 
00166 /*
00167  * Read a section of a file, returning it as text
00168  */
00169 Datum
00170 pg_read_file(PG_FUNCTION_ARGS)
00171 {
00172     text       *filename_t = PG_GETARG_TEXT_P(0);
00173     int64       seek_offset = PG_GETARG_INT64(1);
00174     int64       bytes_to_read = PG_GETARG_INT64(2);
00175     char       *filename;
00176 
00177     if (!superuser())
00178         ereport(ERROR,
00179                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00180                  (errmsg("must be superuser to read files"))));
00181 
00182     filename = convert_and_check_filename(filename_t);
00183 
00184     if (bytes_to_read < 0)
00185         ereport(ERROR,
00186                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00187                  errmsg("requested length cannot be negative")));
00188 
00189     PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
00190 }
00191 
00192 /*
00193  * Read the whole of a file, returning it as text
00194  */
00195 Datum
00196 pg_read_file_all(PG_FUNCTION_ARGS)
00197 {
00198     text       *filename_t = PG_GETARG_TEXT_P(0);
00199     char       *filename;
00200 
00201     if (!superuser())
00202         ereport(ERROR,
00203                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00204                  (errmsg("must be superuser to read files"))));
00205 
00206     filename = convert_and_check_filename(filename_t);
00207 
00208     PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
00209 }
00210 
00211 /*
00212  * Read a section of a file, returning it as bytea
00213  */
00214 Datum
00215 pg_read_binary_file(PG_FUNCTION_ARGS)
00216 {
00217     text       *filename_t = PG_GETARG_TEXT_P(0);
00218     int64       seek_offset = PG_GETARG_INT64(1);
00219     int64       bytes_to_read = PG_GETARG_INT64(2);
00220     char       *filename;
00221 
00222     if (!superuser())
00223         ereport(ERROR,
00224                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00225                  (errmsg("must be superuser to read files"))));
00226 
00227     filename = convert_and_check_filename(filename_t);
00228 
00229     if (bytes_to_read < 0)
00230         ereport(ERROR,
00231                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00232                  errmsg("requested length cannot be negative")));
00233 
00234     PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
00235 }
00236 
00237 /*
00238  * Read the whole of a file, returning it as bytea
00239  */
00240 Datum
00241 pg_read_binary_file_all(PG_FUNCTION_ARGS)
00242 {
00243     text       *filename_t = PG_GETARG_TEXT_P(0);
00244     char       *filename;
00245 
00246     if (!superuser())
00247         ereport(ERROR,
00248                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00249                  (errmsg("must be superuser to read files"))));
00250 
00251     filename = convert_and_check_filename(filename_t);
00252 
00253     PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
00254 }
00255 
00256 /*
00257  * stat a file
00258  */
00259 Datum
00260 pg_stat_file(PG_FUNCTION_ARGS)
00261 {
00262     text       *filename_t = PG_GETARG_TEXT_P(0);
00263     char       *filename;
00264     struct stat fst;
00265     Datum       values[6];
00266     bool        isnull[6];
00267     HeapTuple   tuple;
00268     TupleDesc   tupdesc;
00269 
00270     if (!superuser())
00271         ereport(ERROR,
00272                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00273                  (errmsg("must be superuser to get file information"))));
00274 
00275     filename = convert_and_check_filename(filename_t);
00276 
00277     if (stat(filename, &fst) < 0)
00278         ereport(ERROR,
00279                 (errcode_for_file_access(),
00280                  errmsg("could not stat file \"%s\": %m", filename)));
00281 
00282     /*
00283      * This record type had better match the output parameters declared for me
00284      * in pg_proc.h.
00285      */
00286     tupdesc = CreateTemplateTupleDesc(6, false);
00287     TupleDescInitEntry(tupdesc, (AttrNumber) 1,
00288                        "size", INT8OID, -1, 0);
00289     TupleDescInitEntry(tupdesc, (AttrNumber) 2,
00290                        "access", TIMESTAMPTZOID, -1, 0);
00291     TupleDescInitEntry(tupdesc, (AttrNumber) 3,
00292                        "modification", TIMESTAMPTZOID, -1, 0);
00293     TupleDescInitEntry(tupdesc, (AttrNumber) 4,
00294                        "change", TIMESTAMPTZOID, -1, 0);
00295     TupleDescInitEntry(tupdesc, (AttrNumber) 5,
00296                        "creation", TIMESTAMPTZOID, -1, 0);
00297     TupleDescInitEntry(tupdesc, (AttrNumber) 6,
00298                        "isdir", BOOLOID, -1, 0);
00299     BlessTupleDesc(tupdesc);
00300 
00301     memset(isnull, false, sizeof(isnull));
00302 
00303     values[0] = Int64GetDatum((int64) fst.st_size);
00304     values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
00305     values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
00306     /* Unix has file status change time, while Win32 has creation time */
00307 #if !defined(WIN32) && !defined(__CYGWIN__)
00308     values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
00309     isnull[4] = true;
00310 #else
00311     isnull[3] = true;
00312     values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
00313 #endif
00314     values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
00315 
00316     tuple = heap_form_tuple(tupdesc, values, isnull);
00317 
00318     pfree(filename);
00319 
00320     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
00321 }
00322 
00323 
00324 /*
00325  * List a directory (returns the filenames only)
00326  */
00327 Datum
00328 pg_ls_dir(PG_FUNCTION_ARGS)
00329 {
00330     FuncCallContext *funcctx;
00331     struct dirent *de;
00332     directory_fctx *fctx;
00333 
00334     if (!superuser())
00335         ereport(ERROR,
00336                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00337                  (errmsg("must be superuser to get directory listings"))));
00338 
00339     if (SRF_IS_FIRSTCALL())
00340     {
00341         MemoryContext oldcontext;
00342 
00343         funcctx = SRF_FIRSTCALL_INIT();
00344         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
00345 
00346         fctx = palloc(sizeof(directory_fctx));
00347         fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
00348 
00349         fctx->dirdesc = AllocateDir(fctx->location);
00350 
00351         if (!fctx->dirdesc)
00352             ereport(ERROR,
00353                     (errcode_for_file_access(),
00354                      errmsg("could not open directory \"%s\": %m",
00355                             fctx->location)));
00356 
00357         funcctx->user_fctx = fctx;
00358         MemoryContextSwitchTo(oldcontext);
00359     }
00360 
00361     funcctx = SRF_PERCALL_SETUP();
00362     fctx = (directory_fctx *) funcctx->user_fctx;
00363 
00364     while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
00365     {
00366         if (strcmp(de->d_name, ".") == 0 ||
00367             strcmp(de->d_name, "..") == 0)
00368             continue;
00369 
00370         SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
00371     }
00372 
00373     FreeDir(fctx->dirdesc);
00374 
00375     SRF_RETURN_DONE(funcctx);
00376 }