1 /*-------------------------------------------------------------------------
4 * Functions for direct access to files
7 * Copyright (c) 2004-2012, PostgreSQL Global Development Group
9 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
12 * src/backend/utils/adt/genfile.c
14 *-------------------------------------------------------------------------
23 #include "catalog/pg_type.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"
41 * Convert a "text" filename argument to C string, and check it's allowable.
43 * Filename may be absolute or relative to the DataDir, but we only allow
44 * absolute paths that match DataDir or Log_directory.
47 convert_and_check_filename(text *arg)
51 filename = text_to_cstring(arg);
52 canonicalize_path(filename); /* filename can change length here */
54 if (is_absolute_path(filename))
56 /* Disallow '/a/b/data/..' */
57 if (path_contains_parent_reference(filename))
59 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
60 (errmsg("reference to parent directory (\"..\") not allowed"))));
63 * Allow absolute paths if within DataDir or Log_directory, even
64 * though Log_directory might be outside DataDir.
66 if (!path_is_prefix_of_path(DataDir, filename) &&
67 (!is_absolute_path(Log_directory) ||
68 !path_is_prefix_of_path(Log_directory, filename)))
70 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
71 (errmsg("absolute path not allowed"))));
73 else if (!path_is_relative_and_below_cwd(filename))
75 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
76 (errmsg("path must be in or below the current directory"))));
83 * Read a section of a file, returning it as bytea
85 * Caller is responsible for all permissions checking.
87 * We read the whole of the file when bytes_to_read is negative.
90 read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
96 if (bytes_to_read < 0)
99 bytes_to_read = -seek_offset;
104 if (stat(filename, &fst) < 0)
106 (errcode_for_file_access(),
107 errmsg("could not stat file \"%s\": %m", filename)));
109 bytes_to_read = fst.st_size - seek_offset;
113 /* not sure why anyone thought that int64 length was a good idea */
114 if (bytes_to_read > (MaxAllocSize - VARHDRSZ))
116 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117 errmsg("requested length too large")));
119 if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
121 (errcode_for_file_access(),
122 errmsg("could not open file \"%s\" for reading: %m",
125 if (fseeko(file, (off_t) seek_offset,
126 (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
128 (errcode_for_file_access(),
129 errmsg("could not seek in file \"%s\": %m", filename)));
131 buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
133 nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
137 (errcode_for_file_access(),
138 errmsg("could not read file \"%s\": %m", filename)));
140 SET_VARSIZE(buf, nbytes + VARHDRSZ);
148 * Similar to read_binary_file, but we verify that the contents are valid
149 * in the database encoding.
152 read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
156 buf = read_binary_file(filename, seek_offset, bytes_to_read);
158 /* Make sure the input is valid */
159 pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
161 /* OK, we can cast it to text safely */
166 * Read a section of a file, returning it as text
169 pg_read_file(PG_FUNCTION_ARGS)
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);
178 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
179 (errmsg("must be superuser to read files"))));
181 filename = convert_and_check_filename(filename_t);
183 if (bytes_to_read < 0)
185 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
186 errmsg("requested length cannot be negative")));
188 PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
192 * Read the whole of a file, returning it as text
195 pg_read_file_all(PG_FUNCTION_ARGS)
197 text *filename_t = PG_GETARG_TEXT_P(0);
202 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
203 (errmsg("must be superuser to read files"))));
205 filename = convert_and_check_filename(filename_t);
207 PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
211 * Read a section of a file, returning it as bytea
214 pg_read_binary_file(PG_FUNCTION_ARGS)
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);
223 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
224 (errmsg("must be superuser to read files"))));
226 filename = convert_and_check_filename(filename_t);
228 if (bytes_to_read < 0)
230 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
231 errmsg("requested length cannot be negative")));
233 PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
237 * Read the whole of a file, returning it as bytea
240 pg_read_binary_file_all(PG_FUNCTION_ARGS)
242 text *filename_t = PG_GETARG_TEXT_P(0);
247 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
248 (errmsg("must be superuser to read files"))));
250 filename = convert_and_check_filename(filename_t);
252 PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
259 pg_stat_file(PG_FUNCTION_ARGS)
261 text *filename_t = PG_GETARG_TEXT_P(0);
271 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
272 (errmsg("must be superuser to get file information"))));
274 filename = convert_and_check_filename(filename_t);
276 if (stat(filename, &fst) < 0)
278 (errcode_for_file_access(),
279 errmsg("could not stat file \"%s\": %m", filename)));
282 * This record type had better match the output parameters declared for me
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);
300 memset(isnull, false, sizeof(isnull));
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));
311 values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
313 values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
315 tuple = heap_form_tuple(tupdesc, values, isnull);
319 PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
324 * List a directory (returns the filenames only)
327 pg_ls_dir(PG_FUNCTION_ARGS)
329 FuncCallContext *funcctx;
331 directory_fctx *fctx;
335 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
336 (errmsg("must be superuser to get directory listings"))));
338 if (SRF_IS_FIRSTCALL())
340 MemoryContext oldcontext;
342 funcctx = SRF_FIRSTCALL_INIT();
343 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
345 fctx = palloc(sizeof(directory_fctx));
346 fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
348 fctx->dirdesc = AllocateDir(fctx->location);
352 (errcode_for_file_access(),
353 errmsg("could not open directory \"%s\": %m",
356 funcctx->user_fctx = fctx;
357 MemoryContextSwitchTo(oldcontext);
360 funcctx = SRF_PERCALL_SETUP();
361 fctx = (directory_fctx *) funcctx->user_fctx;
363 while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
365 if (strcmp(de->d_name, ".") == 0 ||
366 strcmp(de->d_name, "..") == 0)
369 SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
372 FreeDir(fctx->dirdesc);
374 SRF_RETURN_DONE(funcctx);