]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/genfile.c
Run pgindent on 9.2 source tree in preparation for first 9.3
[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-2012, 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 "catalog/pg_type.h"
24 #include "funcapi.h"
25 #include "mb/pg_wchar.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         char       *filename;
50
51         filename = text_to_cstring(arg);
52         canonicalize_path(filename);    /* filename can change length here */
53
54         if (is_absolute_path(filename))
55         {
56                 /* Disallow '/a/b/data/..' */
57                 if (path_contains_parent_reference(filename))
58                         ereport(ERROR,
59                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
60                         (errmsg("reference to parent directory (\"..\") not allowed"))));
61
62                 /*
63                  * Allow absolute paths if within DataDir or Log_directory, even
64                  * though Log_directory might be outside DataDir.
65                  */
66                 if (!path_is_prefix_of_path(DataDir, filename) &&
67                         (!is_absolute_path(Log_directory) ||
68                          !path_is_prefix_of_path(Log_directory, filename)))
69                         ereport(ERROR,
70                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
71                                          (errmsg("absolute path not allowed"))));
72         }
73         else if (!path_is_relative_and_below_cwd(filename))
74                 ereport(ERROR,
75                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
76                                  (errmsg("path must be in or below the current directory"))));
77
78         return filename;
79 }
80
81
82 /*
83  * Read a section of a file, returning it as bytea
84  *
85  * Caller is responsible for all permissions checking.
86  *
87  * We read the whole of the file when bytes_to_read is negative.
88  */
89 bytea *
90 read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
91 {
92         bytea      *buf;
93         size_t          nbytes;
94         FILE       *file;
95
96         if (bytes_to_read < 0)
97         {
98                 if (seek_offset < 0)
99                         bytes_to_read = -seek_offset;
100                 else
101                 {
102                         struct stat fst;
103
104                         if (stat(filename, &fst) < 0)
105                                 ereport(ERROR,
106                                                 (errcode_for_file_access(),
107                                                  errmsg("could not stat file \"%s\": %m", filename)));
108
109                         bytes_to_read = fst.st_size - seek_offset;
110                 }
111         }
112
113         /* not sure why anyone thought that int64 length was a good idea */
114         if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
115                 ereport(ERROR,
116                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117                                  errmsg("requested length too large")));
118
119         if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
120                 ereport(ERROR,
121                                 (errcode_for_file_access(),
122                                  errmsg("could not open file \"%s\" for reading: %m",
123                                                 filename)));
124
125         if (fseeko(file, (off_t) seek_offset,
126                            (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
127                 ereport(ERROR,
128                                 (errcode_for_file_access(),
129                                  errmsg("could not seek in file \"%s\": %m", filename)));
130
131         buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
132
133         nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
134
135         if (ferror(file))
136                 ereport(ERROR,
137                                 (errcode_for_file_access(),
138                                  errmsg("could not read file \"%s\": %m", filename)));
139
140         SET_VARSIZE(buf, nbytes + VARHDRSZ);
141
142         FreeFile(file);
143
144         return buf;
145 }
146
147 /*
148  * Similar to read_binary_file, but we verify that the contents are valid
149  * in the database encoding.
150  */
151 static text *
152 read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
153 {
154         bytea      *buf;
155
156         buf = read_binary_file(filename, seek_offset, bytes_to_read);
157
158         /* Make sure the input is valid */
159         pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
160
161         /* OK, we can cast it to text safely */
162         return (text *) buf;
163 }
164
165 /*
166  * Read a section of a file, returning it as text
167  */
168 Datum
169 pg_read_file(PG_FUNCTION_ARGS)
170 {
171         text       *filename_t = PG_GETARG_TEXT_P(0);
172         int64           seek_offset = PG_GETARG_INT64(1);
173         int64           bytes_to_read = PG_GETARG_INT64(2);
174         char       *filename;
175
176         if (!superuser())
177                 ereport(ERROR,
178                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
179                                  (errmsg("must be superuser to read files"))));
180
181         filename = convert_and_check_filename(filename_t);
182
183         if (bytes_to_read < 0)
184                 ereport(ERROR,
185                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
186                                  errmsg("requested length cannot be negative")));
187
188         PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
189 }
190
191 /*
192  * Read the whole of a file, returning it as text
193  */
194 Datum
195 pg_read_file_all(PG_FUNCTION_ARGS)
196 {
197         text       *filename_t = PG_GETARG_TEXT_P(0);
198         char       *filename;
199
200         if (!superuser())
201                 ereport(ERROR,
202                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
203                                  (errmsg("must be superuser to read files"))));
204
205         filename = convert_and_check_filename(filename_t);
206
207         PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
208 }
209
210 /*
211  * Read a section of a file, returning it as bytea
212  */
213 Datum
214 pg_read_binary_file(PG_FUNCTION_ARGS)
215 {
216         text       *filename_t = PG_GETARG_TEXT_P(0);
217         int64           seek_offset = PG_GETARG_INT64(1);
218         int64           bytes_to_read = PG_GETARG_INT64(2);
219         char       *filename;
220
221         if (!superuser())
222                 ereport(ERROR,
223                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
224                                  (errmsg("must be superuser to read files"))));
225
226         filename = convert_and_check_filename(filename_t);
227
228         if (bytes_to_read < 0)
229                 ereport(ERROR,
230                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
231                                  errmsg("requested length cannot be negative")));
232
233         PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
234 }
235
236 /*
237  * Read the whole of a file, returning it as bytea
238  */
239 Datum
240 pg_read_binary_file_all(PG_FUNCTION_ARGS)
241 {
242         text       *filename_t = PG_GETARG_TEXT_P(0);
243         char       *filename;
244
245         if (!superuser())
246                 ereport(ERROR,
247                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
248                                  (errmsg("must be superuser to read files"))));
249
250         filename = convert_and_check_filename(filename_t);
251
252         PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
253 }
254
255 /*
256  * stat a file
257  */
258 Datum
259 pg_stat_file(PG_FUNCTION_ARGS)
260 {
261         text       *filename_t = PG_GETARG_TEXT_P(0);
262         char       *filename;
263         struct stat fst;
264         Datum           values[6];
265         bool            isnull[6];
266         HeapTuple       tuple;
267         TupleDesc       tupdesc;
268
269         if (!superuser())
270                 ereport(ERROR,
271                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
272                                  (errmsg("must be superuser to get file information"))));
273
274         filename = convert_and_check_filename(filename_t);
275
276         if (stat(filename, &fst) < 0)
277                 ereport(ERROR,
278                                 (errcode_for_file_access(),
279                                  errmsg("could not stat file \"%s\": %m", filename)));
280
281         /*
282          * This record type had better match the output parameters declared for me
283          * in pg_proc.h.
284          */
285         tupdesc = CreateTemplateTupleDesc(6, false);
286         TupleDescInitEntry(tupdesc, (AttrNumber) 1,
287                                            "size", INT8OID, -1, 0);
288         TupleDescInitEntry(tupdesc, (AttrNumber) 2,
289                                            "access", TIMESTAMPTZOID, -1, 0);
290         TupleDescInitEntry(tupdesc, (AttrNumber) 3,
291                                            "modification", TIMESTAMPTZOID, -1, 0);
292         TupleDescInitEntry(tupdesc, (AttrNumber) 4,
293                                            "change", TIMESTAMPTZOID, -1, 0);
294         TupleDescInitEntry(tupdesc, (AttrNumber) 5,
295                                            "creation", TIMESTAMPTZOID, -1, 0);
296         TupleDescInitEntry(tupdesc, (AttrNumber) 6,
297                                            "isdir", BOOLOID, -1, 0);
298         BlessTupleDesc(tupdesc);
299
300         memset(isnull, false, sizeof(isnull));
301
302         values[0] = Int64GetDatum((int64) fst.st_size);
303         values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
304         values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
305         /* Unix has file status change time, while Win32 has creation time */
306 #if !defined(WIN32) && !defined(__CYGWIN__)
307         values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
308         isnull[4] = true;
309 #else
310         isnull[3] = true;
311         values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
312 #endif
313         values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
314
315         tuple = heap_form_tuple(tupdesc, values, isnull);
316
317         pfree(filename);
318
319         PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
320 }
321
322
323 /*
324  * List a directory (returns the filenames only)
325  */
326 Datum
327 pg_ls_dir(PG_FUNCTION_ARGS)
328 {
329         FuncCallContext *funcctx;
330         struct dirent *de;
331         directory_fctx *fctx;
332
333         if (!superuser())
334                 ereport(ERROR,
335                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
336                                  (errmsg("must be superuser to get directory listings"))));
337
338         if (SRF_IS_FIRSTCALL())
339         {
340                 MemoryContext oldcontext;
341
342                 funcctx = SRF_FIRSTCALL_INIT();
343                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
344
345                 fctx = palloc(sizeof(directory_fctx));
346                 fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
347
348                 fctx->dirdesc = AllocateDir(fctx->location);
349
350                 if (!fctx->dirdesc)
351                         ereport(ERROR,
352                                         (errcode_for_file_access(),
353                                          errmsg("could not open directory \"%s\": %m",
354                                                         fctx->location)));
355
356                 funcctx->user_fctx = fctx;
357                 MemoryContextSwitchTo(oldcontext);
358         }
359
360         funcctx = SRF_PERCALL_SETUP();
361         fctx = (directory_fctx *) funcctx->user_fctx;
362
363         while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
364         {
365                 if (strcmp(de->d_name, ".") == 0 ||
366                         strcmp(de->d_name, "..") == 0)
367                         continue;
368
369                 SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
370         }
371
372         FreeDir(fctx->dirdesc);
373
374         SRF_RETURN_DONE(funcctx);
375 }