]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/genfile.c
38a129783ea3c91122808566a4ae91eb00449d2a
[postgresql] / src / backend / utils / adt / genfile.c
1 /*-------------------------------------------------------------------------
2  *
3  * genfile.c
4  *              Functions for direct access to files
5  *
6  *
7  * Copyright (c) 2004-2006, PostgreSQL Global Development Group
8  *
9  * Author: Andreas Pflug <pgadmin@pse-consulting.de>
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/utils/adt/genfile.c,v 1.13 2006/11/24 21:18:42 tgl Exp $
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/heapam.h"
24 #include "catalog/pg_type.h"
25 #include "funcapi.h"
26 #include "miscadmin.h"
27 #include "postmaster/syslogger.h"
28 #include "storage/fd.h"
29 #include "utils/builtins.h"
30 #include "utils/memutils.h"
31 #include "utils/timestamp.h"
32
33 typedef struct
34 {
35         char       *location;
36         DIR                *dirdesc;
37 } directory_fctx;
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 static char *
47 convert_and_check_filename(text *arg)
48 {
49         int                     input_len = VARSIZE(arg) - VARHDRSZ;
50         char       *filename = palloc(input_len + 1);
51
52         memcpy(filename, VARDATA(arg), input_len);
53         filename[input_len] = '\0';
54
55         canonicalize_path(filename);    /* filename can change length here */
56
57         /* Disallow ".." in the path */
58         if (path_contains_parent_reference(filename))
59                 ereport(ERROR,
60                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
61                         (errmsg("reference to parent directory (\"..\") not allowed"))));
62
63         if (is_absolute_path(filename))
64         {
65                 /* Allow absolute references within DataDir */
66                 if (path_is_prefix_of_path(DataDir, filename))
67                         return filename;
68                 /* The log directory might be outside our datadir, but allow it */
69                 if (is_absolute_path(Log_directory) &&
70                         path_is_prefix_of_path(Log_directory, filename))
71                         return filename;
72
73                 ereport(ERROR,
74                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
75                                  (errmsg("absolute path not allowed"))));
76                 return NULL;                    /* keep compiler quiet */
77         }
78         else
79         {
80                 return filename;
81         }
82 }
83
84
85 /*
86  * Read a section of a file, returning it as text
87  */
88 Datum
89 pg_read_file(PG_FUNCTION_ARGS)
90 {
91         text       *filename_t = PG_GETARG_TEXT_P(0);
92         int64           seek_offset = PG_GETARG_INT64(1);
93         int64           bytes_to_read = PG_GETARG_INT64(2);
94         char       *buf;
95         size_t          nbytes;
96         FILE       *file;
97         char       *filename;
98
99         if (!superuser())
100                 ereport(ERROR,
101                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
102                                  (errmsg("must be superuser to read files"))));
103
104         filename = convert_and_check_filename(filename_t);
105
106         if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
107                 ereport(ERROR,
108                                 (errcode_for_file_access(),
109                                  errmsg("could not open file \"%s\" for reading: %m",
110                                                 filename)));
111
112         if (fseeko(file, (off_t) seek_offset,
113                            (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
114                 ereport(ERROR,
115                                 (errcode_for_file_access(),
116                                  errmsg("could not seek in file \"%s\": %m", filename)));
117
118         if (bytes_to_read < 0)
119                 ereport(ERROR,
120                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
121                                  errmsg("requested length may not be negative")));
122
123         /* not sure why anyone thought that int64 length was a good idea */
124         if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
125                 ereport(ERROR,
126                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
127                                  errmsg("requested length too large")));
128
129         buf = palloc((Size) bytes_to_read + VARHDRSZ);
130
131         nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
132
133         if (ferror(file))
134                 ereport(ERROR,
135                                 (errcode_for_file_access(),
136                                  errmsg("could not read file \"%s\": %m", filename)));
137
138         VARATT_SIZEP(buf) = nbytes + VARHDRSZ;
139
140         FreeFile(file);
141         pfree(filename);
142
143         PG_RETURN_TEXT_P(buf);
144 }
145
146 /*
147  * stat a file
148  */
149 Datum
150 pg_stat_file(PG_FUNCTION_ARGS)
151 {
152         text       *filename_t = PG_GETARG_TEXT_P(0);
153         char       *filename;
154         struct stat fst;
155         Datum           values[6];
156         bool            isnull[6];
157         HeapTuple       tuple;
158         TupleDesc       tupdesc;
159
160         if (!superuser())
161                 ereport(ERROR,
162                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
163                                  (errmsg("must be superuser to get file information"))));
164
165         filename = convert_and_check_filename(filename_t);
166
167         if (stat(filename, &fst) < 0)
168                 ereport(ERROR,
169                                 (errcode_for_file_access(),
170                                  errmsg("could not stat file \"%s\": %m", filename)));
171
172         /*
173          * This record type had better match the output parameters declared for me
174          * in pg_proc.h.
175          */
176         tupdesc = CreateTemplateTupleDesc(6, false);
177         TupleDescInitEntry(tupdesc, (AttrNumber) 1,
178                                            "size", INT8OID, -1, 0);
179         TupleDescInitEntry(tupdesc, (AttrNumber) 2,
180                                            "access", TIMESTAMPTZOID, -1, 0);
181         TupleDescInitEntry(tupdesc, (AttrNumber) 3,
182                                            "modification", TIMESTAMPTZOID, -1, 0);
183         TupleDescInitEntry(tupdesc, (AttrNumber) 4,
184                                            "change", TIMESTAMPTZOID, -1, 0);
185         TupleDescInitEntry(tupdesc, (AttrNumber) 5,
186                                            "creation", TIMESTAMPTZOID, -1, 0);
187         TupleDescInitEntry(tupdesc, (AttrNumber) 6,
188                                            "isdir", BOOLOID, -1, 0);
189         BlessTupleDesc(tupdesc);
190
191         memset(isnull, false, sizeof(isnull));
192
193         values[0] = Int64GetDatum((int64) fst.st_size);
194         values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
195         values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
196         /* Unix has file status change time, while Win32 has creation time */
197 #if !defined(WIN32) && !defined(__CYGWIN__)
198         values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
199         isnull[4] = true;
200 #else
201         isnull[3] = true;
202         values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
203 #endif
204         values[5] = BoolGetDatum(fst.st_mode & S_IFDIR);
205
206         tuple = heap_form_tuple(tupdesc, values, isnull);
207
208         pfree(filename);
209
210         PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
211 }
212
213
214 /*
215  * List a directory (returns the filenames only)
216  */
217 Datum
218 pg_ls_dir(PG_FUNCTION_ARGS)
219 {
220         FuncCallContext *funcctx;
221         struct dirent *de;
222         directory_fctx *fctx;
223
224         if (!superuser())
225                 ereport(ERROR,
226                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
227                                  (errmsg("must be superuser to get directory listings"))));
228
229         if (SRF_IS_FIRSTCALL())
230         {
231                 MemoryContext oldcontext;
232
233                 funcctx = SRF_FIRSTCALL_INIT();
234                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
235
236                 fctx = palloc(sizeof(directory_fctx));
237                 fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
238
239                 fctx->dirdesc = AllocateDir(fctx->location);
240
241                 if (!fctx->dirdesc)
242                         ereport(ERROR,
243                                         (errcode_for_file_access(),
244                                          errmsg("could not open directory \"%s\": %m",
245                                                         fctx->location)));
246
247                 funcctx->user_fctx = fctx;
248                 MemoryContextSwitchTo(oldcontext);
249         }
250
251         funcctx = SRF_PERCALL_SETUP();
252         fctx = (directory_fctx *) funcctx->user_fctx;
253
254         while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
255         {
256                 int                     len = strlen(de->d_name);
257                 text       *result;
258
259                 if (strcmp(de->d_name, ".") == 0 ||
260                         strcmp(de->d_name, "..") == 0)
261                         continue;
262
263                 result = palloc(len + VARHDRSZ);
264                 VARATT_SIZEP(result) = len + VARHDRSZ;
265                 memcpy(VARDATA(result), de->d_name, len);
266
267                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(result));
268         }
269
270         FreeDir(fctx->dirdesc);
271
272         SRF_RETURN_DONE(funcctx);
273 }