Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * genfile.c
4 : * Functions for direct access to files
5 : *
6 : *
7 : * Copyright (c) 2004-2021, 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_authid.h"
26 : #include "catalog/pg_tablespace_d.h"
27 : #include "catalog/pg_type.h"
28 : #include "funcapi.h"
29 : #include "mb/pg_wchar.h"
30 : #include "miscadmin.h"
31 : #include "postmaster/syslogger.h"
32 : #include "storage/fd.h"
33 : #include "utils/acl.h"
34 : #include "utils/builtins.h"
35 : #include "utils/memutils.h"
36 : #include "utils/syscache.h"
37 : #include "utils/timestamp.h"
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 : * This does a privilege check against the 'pg_read_server_files' role, so
47 : * this function is really only appropriate for callers who are only checking
48 : * 'read' access. Do not use this function if you are looking for a check
49 : * for 'write' or 'program' access without updating it to access the type
50 : * of check as an argument and checking the appropriate role membership.
51 : */
52 : static char *
53 18240 : convert_and_check_filename(text *arg)
54 : {
55 : char *filename;
56 :
57 18240 : filename = text_to_cstring(arg);
58 18240 : canonicalize_path(filename); /* filename can change length here */
59 :
60 : /*
61 : * Members of the 'pg_read_server_files' role are allowed to access any
62 : * files on the server as the PG user, so no need to do any further checks
63 : * here.
64 : */
65 18240 : if (is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_SERVER_FILES))
66 5254 : return filename;
67 :
68 : /* User isn't a member of the default role, so check if it's allowable */
69 12986 : if (is_absolute_path(filename))
70 : {
71 : /* Disallow '/a/b/data/..' */
72 0 : if (path_contains_parent_reference(filename))
73 0 : ereport(ERROR,
74 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
75 : errmsg("reference to parent directory (\"..\") not allowed")));
76 :
77 : /*
78 : * Allow absolute paths if within DataDir or Log_directory, even
79 : * though Log_directory might be outside DataDir.
80 : */
81 0 : if (!path_is_prefix_of_path(DataDir, filename) &&
82 0 : (!is_absolute_path(Log_directory) ||
83 0 : !path_is_prefix_of_path(Log_directory, filename)))
84 0 : ereport(ERROR,
85 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
86 : errmsg("absolute path not allowed")));
87 : }
88 12986 : else if (!path_is_relative_and_below_cwd(filename))
89 0 : ereport(ERROR,
90 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
91 : errmsg("path must be in or below the current directory")));
92 :
93 12986 : return filename;
94 : }
95 :
96 :
97 : /*
98 : * Read a section of a file, returning it as bytea
99 : *
100 : * Caller is responsible for all permissions checking.
101 : *
102 : * We read the whole of the file when bytes_to_read is negative.
103 : */
104 : static bytea *
105 4398 : read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
106 : bool missing_ok)
107 : {
108 : bytea *buf;
109 4398 : size_t nbytes = 0;
110 : FILE *file;
111 :
112 : /* clamp request size to what we can actually deliver */
113 4398 : if (bytes_to_read > (int64) (MaxAllocSize - VARHDRSZ))
114 0 : ereport(ERROR,
115 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
116 : errmsg("requested length too large")));
117 :
118 4398 : if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
119 : {
120 4 : if (missing_ok && errno == ENOENT)
121 0 : return NULL;
122 : else
123 4 : ereport(ERROR,
124 : (errcode_for_file_access(),
125 : errmsg("could not open file \"%s\" for reading: %m",
126 : filename)));
127 : }
128 :
129 4394 : if (fseeko(file, (off_t) seek_offset,
130 : (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
131 0 : ereport(ERROR,
132 : (errcode_for_file_access(),
133 : errmsg("could not seek in file \"%s\": %m", filename)));
134 :
135 4394 : if (bytes_to_read >= 0)
136 : {
137 : /* If passed explicit read size just do it */
138 4348 : buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ);
139 :
140 4348 : nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file);
141 : }
142 : else
143 : {
144 : /* Negative read size, read rest of file */
145 : StringInfoData sbuf;
146 :
147 46 : initStringInfo(&sbuf);
148 : /* Leave room in the buffer for the varlena length word */
149 46 : sbuf.len += VARHDRSZ;
150 : Assert(sbuf.len < sbuf.maxlen);
151 :
152 116 : while (!(feof(file) || ferror(file)))
153 : {
154 : size_t rbytes;
155 :
156 : /* Minimum amount to read at a time */
157 : #define MIN_READ_SIZE 4096
158 :
159 : /*
160 : * If not at end of file, and sbuf.len is equal to
161 : * MaxAllocSize - 1, then either the file is too large, or
162 : * there is nothing left to read. Attempt to read one more
163 : * byte to see if the end of file has been reached. If not,
164 : * the file is too large; we'd rather give the error message
165 : * for that ourselves.
166 : */
167 70 : if (sbuf.len == MaxAllocSize - 1)
168 : {
169 : char rbuf[1];
170 :
171 0 : if (fread(rbuf, 1, 1, file) != 0 || !feof(file))
172 0 : ereport(ERROR,
173 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
174 : errmsg("file length too large")));
175 : else
176 : break;
177 : }
178 :
179 : /* OK, ensure that we can read at least MIN_READ_SIZE */
180 70 : enlargeStringInfo(&sbuf, MIN_READ_SIZE);
181 :
182 : /*
183 : * stringinfo.c likes to allocate in powers of 2, so it's likely
184 : * that much more space is available than we asked for. Use all
185 : * of it, rather than making more fread calls than necessary.
186 : */
187 70 : rbytes = fread(sbuf.data + sbuf.len, 1,
188 70 : (size_t) (sbuf.maxlen - sbuf.len - 1), file);
189 70 : sbuf.len += rbytes;
190 70 : nbytes += rbytes;
191 : }
192 :
193 : /* Now we can commandeer the stringinfo's buffer as the result */
194 46 : buf = (bytea *) sbuf.data;
195 : }
196 :
197 4394 : if (ferror(file))
198 0 : ereport(ERROR,
199 : (errcode_for_file_access(),
200 : errmsg("could not read file \"%s\": %m", filename)));
201 :
202 4394 : SET_VARSIZE(buf, nbytes + VARHDRSZ);
203 :
204 4394 : FreeFile(file);
205 :
206 4394 : return buf;
207 : }
208 :
209 : /*
210 : * Similar to read_binary_file, but we verify that the contents are valid
211 : * in the database encoding.
212 : */
213 : static text *
214 16 : read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
215 : bool missing_ok)
216 : {
217 : bytea *buf;
218 :
219 16 : buf = read_binary_file(filename, seek_offset, bytes_to_read, missing_ok);
220 :
221 12 : if (buf != NULL)
222 : {
223 : /* Make sure the input is valid */
224 12 : pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
225 :
226 : /* OK, we can cast it to text safely */
227 12 : return (text *) buf;
228 : }
229 : else
230 0 : return NULL;
231 : }
232 :
233 : /*
234 : * Read a section of a file, returning it as text
235 : *
236 : * This function is kept to support adminpack 1.0.
237 : */
238 : Datum
239 0 : pg_read_file(PG_FUNCTION_ARGS)
240 : {
241 0 : text *filename_t = PG_GETARG_TEXT_PP(0);
242 0 : int64 seek_offset = 0;
243 0 : int64 bytes_to_read = -1;
244 0 : bool missing_ok = false;
245 : char *filename;
246 : text *result;
247 :
248 0 : if (!superuser())
249 0 : ereport(ERROR,
250 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
251 : errmsg("must be superuser to read files with adminpack 1.0"),
252 : /* translator: %s is a SQL function name */
253 : errhint("Consider using %s, which is part of core, instead.",
254 : "pg_file_read()")));
255 :
256 : /* handle optional arguments */
257 0 : if (PG_NARGS() >= 3)
258 : {
259 0 : seek_offset = PG_GETARG_INT64(1);
260 0 : bytes_to_read = PG_GETARG_INT64(2);
261 :
262 0 : if (bytes_to_read < 0)
263 0 : ereport(ERROR,
264 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
265 : errmsg("requested length cannot be negative")));
266 : }
267 0 : if (PG_NARGS() >= 4)
268 0 : missing_ok = PG_GETARG_BOOL(3);
269 :
270 0 : filename = convert_and_check_filename(filename_t);
271 :
272 0 : result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
273 0 : if (result)
274 0 : PG_RETURN_TEXT_P(result);
275 : else
276 0 : PG_RETURN_NULL();
277 : }
278 :
279 : /*
280 : * Read a section of a file, returning it as text
281 : *
282 : * No superuser check done here- instead privileges are handled by the
283 : * GRANT system.
284 : */
285 : Datum
286 16 : pg_read_file_v2(PG_FUNCTION_ARGS)
287 : {
288 16 : text *filename_t = PG_GETARG_TEXT_PP(0);
289 16 : int64 seek_offset = 0;
290 16 : int64 bytes_to_read = -1;
291 16 : bool missing_ok = false;
292 : char *filename;
293 : text *result;
294 :
295 : /* handle optional arguments */
296 16 : if (PG_NARGS() >= 3)
297 : {
298 0 : seek_offset = PG_GETARG_INT64(1);
299 0 : bytes_to_read = PG_GETARG_INT64(2);
300 :
301 0 : if (bytes_to_read < 0)
302 0 : ereport(ERROR,
303 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
304 : errmsg("requested length cannot be negative")));
305 : }
306 16 : if (PG_NARGS() >= 4)
307 0 : missing_ok = PG_GETARG_BOOL(3);
308 :
309 16 : filename = convert_and_check_filename(filename_t);
310 :
311 16 : result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
312 12 : if (result)
313 12 : PG_RETURN_TEXT_P(result);
314 : else
315 0 : PG_RETURN_NULL();
316 : }
317 :
318 : /*
319 : * Read a section of a file, returning it as bytea
320 : */
321 : Datum
322 4382 : pg_read_binary_file(PG_FUNCTION_ARGS)
323 : {
324 4382 : text *filename_t = PG_GETARG_TEXT_PP(0);
325 4382 : int64 seek_offset = 0;
326 4382 : int64 bytes_to_read = -1;
327 4382 : bool missing_ok = false;
328 : char *filename;
329 : bytea *result;
330 :
331 : /* handle optional arguments */
332 4382 : if (PG_NARGS() >= 3)
333 : {
334 4348 : seek_offset = PG_GETARG_INT64(1);
335 4348 : bytes_to_read = PG_GETARG_INT64(2);
336 :
337 4348 : if (bytes_to_read < 0)
338 0 : ereport(ERROR,
339 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
340 : errmsg("requested length cannot be negative")));
341 : }
342 4382 : if (PG_NARGS() >= 4)
343 4348 : missing_ok = PG_GETARG_BOOL(3);
344 :
345 4382 : filename = convert_and_check_filename(filename_t);
346 :
347 4382 : result = read_binary_file(filename, seek_offset,
348 : bytes_to_read, missing_ok);
349 4382 : if (result)
350 4382 : PG_RETURN_BYTEA_P(result);
351 : else
352 0 : PG_RETURN_NULL();
353 : }
354 :
355 :
356 : /*
357 : * Wrapper functions for the 1 and 3 argument variants of pg_read_file_v2()
358 : * and pg_read_binary_file().
359 : *
360 : * These are necessary to pass the sanity check in opr_sanity, which checks
361 : * that all built-in functions that share the implementing C function take
362 : * the same number of arguments.
363 : */
364 : Datum
365 0 : pg_read_file_off_len(PG_FUNCTION_ARGS)
366 : {
367 0 : return pg_read_file_v2(fcinfo);
368 : }
369 :
370 : Datum
371 16 : pg_read_file_all(PG_FUNCTION_ARGS)
372 : {
373 16 : return pg_read_file_v2(fcinfo);
374 : }
375 :
376 : Datum
377 0 : pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
378 : {
379 0 : return pg_read_binary_file(fcinfo);
380 : }
381 :
382 : Datum
383 34 : pg_read_binary_file_all(PG_FUNCTION_ARGS)
384 : {
385 34 : return pg_read_binary_file(fcinfo);
386 : }
387 :
388 : /*
389 : * stat a file
390 : */
391 : Datum
392 13508 : pg_stat_file(PG_FUNCTION_ARGS)
393 : {
394 13508 : text *filename_t = PG_GETARG_TEXT_PP(0);
395 : char *filename;
396 : struct stat fst;
397 : Datum values[6];
398 : bool isnull[6];
399 : HeapTuple tuple;
400 : TupleDesc tupdesc;
401 13508 : bool missing_ok = false;
402 :
403 : /* check the optional argument */
404 13508 : if (PG_NARGS() == 2)
405 13508 : missing_ok = PG_GETARG_BOOL(1);
406 :
407 13508 : filename = convert_and_check_filename(filename_t);
408 :
409 13508 : if (stat(filename, &fst) < 0)
410 : {
411 0 : if (missing_ok && errno == ENOENT)
412 0 : PG_RETURN_NULL();
413 : else
414 0 : ereport(ERROR,
415 : (errcode_for_file_access(),
416 : errmsg("could not stat file \"%s\": %m", filename)));
417 : }
418 :
419 : /*
420 : * This record type had better match the output parameters declared for me
421 : * in pg_proc.h.
422 : */
423 13508 : tupdesc = CreateTemplateTupleDesc(6);
424 13508 : TupleDescInitEntry(tupdesc, (AttrNumber) 1,
425 : "size", INT8OID, -1, 0);
426 13508 : TupleDescInitEntry(tupdesc, (AttrNumber) 2,
427 : "access", TIMESTAMPTZOID, -1, 0);
428 13508 : TupleDescInitEntry(tupdesc, (AttrNumber) 3,
429 : "modification", TIMESTAMPTZOID, -1, 0);
430 13508 : TupleDescInitEntry(tupdesc, (AttrNumber) 4,
431 : "change", TIMESTAMPTZOID, -1, 0);
432 13508 : TupleDescInitEntry(tupdesc, (AttrNumber) 5,
433 : "creation", TIMESTAMPTZOID, -1, 0);
434 13508 : TupleDescInitEntry(tupdesc, (AttrNumber) 6,
435 : "isdir", BOOLOID, -1, 0);
436 13508 : BlessTupleDesc(tupdesc);
437 :
438 13508 : memset(isnull, false, sizeof(isnull));
439 :
440 13508 : values[0] = Int64GetDatum((int64) fst.st_size);
441 13508 : values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
442 13508 : values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
443 : /* Unix has file status change time, while Win32 has creation time */
444 : #if !defined(WIN32) && !defined(__CYGWIN__)
445 13508 : values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
446 13508 : isnull[4] = true;
447 : #else
448 : isnull[3] = true;
449 : values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
450 : #endif
451 13508 : values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
452 :
453 13508 : tuple = heap_form_tuple(tupdesc, values, isnull);
454 :
455 13508 : pfree(filename);
456 :
457 13508 : PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
458 : }
459 :
460 : /*
461 : * stat a file (1 argument version)
462 : *
463 : * note: this wrapper is necessary to pass the sanity check in opr_sanity,
464 : * which checks that all built-in functions that share the implementing C
465 : * function take the same number of arguments
466 : */
467 : Datum
468 0 : pg_stat_file_1arg(PG_FUNCTION_ARGS)
469 : {
470 0 : return pg_stat_file(fcinfo);
471 : }
472 :
473 : /*
474 : * List a directory (returns the filenames only)
475 : */
476 : Datum
477 334 : pg_ls_dir(PG_FUNCTION_ARGS)
478 : {
479 334 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
480 : char *location;
481 334 : bool missing_ok = false;
482 334 : bool include_dot_dirs = false;
483 : bool randomAccess;
484 : TupleDesc tupdesc;
485 : Tuplestorestate *tupstore;
486 : DIR *dirdesc;
487 : struct dirent *de;
488 : MemoryContext oldcontext;
489 :
490 334 : location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
491 :
492 : /* check the optional arguments */
493 334 : if (PG_NARGS() == 3)
494 : {
495 330 : if (!PG_ARGISNULL(1))
496 330 : missing_ok = PG_GETARG_BOOL(1);
497 330 : if (!PG_ARGISNULL(2))
498 330 : include_dot_dirs = PG_GETARG_BOOL(2);
499 : }
500 :
501 : /* check to see if caller supports us returning a tuplestore */
502 334 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
503 0 : ereport(ERROR,
504 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
505 : errmsg("set-valued function called in context that cannot accept a set")));
506 334 : if (!(rsinfo->allowedModes & SFRM_Materialize))
507 0 : ereport(ERROR,
508 : (errcode(ERRCODE_SYNTAX_ERROR),
509 : errmsg("materialize mode required, but it is not allowed in this context")));
510 :
511 : /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
512 334 : oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
513 :
514 334 : tupdesc = CreateTemplateTupleDesc(1);
515 334 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_ls_dir", TEXTOID, -1, 0);
516 :
517 334 : randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
518 334 : tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
519 334 : rsinfo->returnMode = SFRM_Materialize;
520 334 : rsinfo->setResult = tupstore;
521 334 : rsinfo->setDesc = tupdesc;
522 :
523 334 : MemoryContextSwitchTo(oldcontext);
524 :
525 334 : dirdesc = AllocateDir(location);
526 334 : if (!dirdesc)
527 : {
528 : /* Return empty tuplestore if appropriate */
529 0 : if (missing_ok && errno == ENOENT)
530 0 : return (Datum) 0;
531 : /* Otherwise, we can let ReadDir() throw the error */
532 : }
533 :
534 14606 : while ((de = ReadDir(dirdesc, location)) != NULL)
535 : {
536 : Datum values[1];
537 : bool nulls[1];
538 :
539 14272 : if (!include_dot_dirs &&
540 14272 : (strcmp(de->d_name, ".") == 0 ||
541 13938 : strcmp(de->d_name, "..") == 0))
542 668 : continue;
543 :
544 13604 : values[0] = CStringGetTextDatum(de->d_name);
545 13604 : nulls[0] = false;
546 :
547 13604 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
548 : }
549 :
550 334 : FreeDir(dirdesc);
551 334 : return (Datum) 0;
552 : }
553 :
554 : /*
555 : * List a directory (1 argument version)
556 : *
557 : * note: this wrapper is necessary to pass the sanity check in opr_sanity,
558 : * which checks that all built-in functions that share the implementing C
559 : * function take the same number of arguments.
560 : */
561 : Datum
562 4 : pg_ls_dir_1arg(PG_FUNCTION_ARGS)
563 : {
564 4 : return pg_ls_dir(fcinfo);
565 : }
566 :
567 : /*
568 : * Generic function to return a directory listing of files.
569 : *
570 : * If the directory isn't there, silently return an empty set if missing_ok.
571 : * Other unreadable-directory cases throw an error.
572 : */
573 : static Datum
574 20 : pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
575 : {
576 20 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
577 : bool randomAccess;
578 : TupleDesc tupdesc;
579 : Tuplestorestate *tupstore;
580 : DIR *dirdesc;
581 : struct dirent *de;
582 : MemoryContext oldcontext;
583 :
584 : /* check to see if caller supports us returning a tuplestore */
585 20 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
586 0 : ereport(ERROR,
587 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
588 : errmsg("set-valued function called in context that cannot accept a set")));
589 20 : if (!(rsinfo->allowedModes & SFRM_Materialize))
590 0 : ereport(ERROR,
591 : (errcode(ERRCODE_SYNTAX_ERROR),
592 : errmsg("materialize mode required, but it is not allowed in this context")));
593 :
594 : /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
595 20 : oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
596 :
597 20 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
598 0 : elog(ERROR, "return type must be a row type");
599 :
600 20 : randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
601 20 : tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
602 20 : rsinfo->returnMode = SFRM_Materialize;
603 20 : rsinfo->setResult = tupstore;
604 20 : rsinfo->setDesc = tupdesc;
605 :
606 20 : MemoryContextSwitchTo(oldcontext);
607 :
608 : /*
609 : * Now walk the directory. Note that we must do this within a single SRF
610 : * call, not leave the directory open across multiple calls, since we
611 : * can't count on the SRF being run to completion.
612 : */
613 20 : dirdesc = AllocateDir(dir);
614 20 : if (!dirdesc)
615 : {
616 : /* Return empty tuplestore if appropriate */
617 0 : if (missing_ok && errno == ENOENT)
618 0 : return (Datum) 0;
619 : /* Otherwise, we can let ReadDir() throw the error */
620 : }
621 :
622 660 : while ((de = ReadDir(dirdesc, dir)) != NULL)
623 : {
624 : Datum values[3];
625 : bool nulls[3];
626 : char path[MAXPGPATH * 2];
627 : struct stat attrib;
628 :
629 : /* Skip hidden files */
630 640 : if (de->d_name[0] == '.')
631 56 : continue;
632 :
633 : /* Get the file info */
634 600 : snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
635 600 : if (stat(path, &attrib) < 0)
636 : {
637 : /* Ignore concurrently-deleted files, else complain */
638 0 : if (errno == ENOENT)
639 0 : continue;
640 0 : ereport(ERROR,
641 : (errcode_for_file_access(),
642 : errmsg("could not stat file \"%s\": %m", path)));
643 : }
644 :
645 : /* Ignore anything but regular files */
646 600 : if (!S_ISREG(attrib.st_mode))
647 16 : continue;
648 :
649 584 : values[0] = CStringGetTextDatum(de->d_name);
650 584 : values[1] = Int64GetDatum((int64) attrib.st_size);
651 584 : values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
652 584 : memset(nulls, 0, sizeof(nulls));
653 :
654 584 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
655 : }
656 :
657 20 : FreeDir(dirdesc);
658 20 : return (Datum) 0;
659 : }
660 :
661 : /* Function to return the list of files in the log directory */
662 : Datum
663 0 : pg_ls_logdir(PG_FUNCTION_ARGS)
664 : {
665 0 : return pg_ls_dir_files(fcinfo, Log_directory, false);
666 : }
667 :
668 : /* Function to return the list of files in the WAL directory */
669 : Datum
670 16 : pg_ls_waldir(PG_FUNCTION_ARGS)
671 : {
672 16 : return pg_ls_dir_files(fcinfo, XLOGDIR, false);
673 : }
674 :
675 : /*
676 : * Generic function to return the list of files in pgsql_tmp
677 : */
678 : static Datum
679 0 : pg_ls_tmpdir(FunctionCallInfo fcinfo, Oid tblspc)
680 : {
681 : char path[MAXPGPATH];
682 :
683 0 : if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tblspc)))
684 0 : ereport(ERROR,
685 : (errcode(ERRCODE_UNDEFINED_OBJECT),
686 : errmsg("tablespace with OID %u does not exist",
687 : tblspc)));
688 :
689 0 : TempTablespacePath(path, tblspc);
690 0 : return pg_ls_dir_files(fcinfo, path, true);
691 : }
692 :
693 : /*
694 : * Function to return the list of temporary files in the pg_default tablespace's
695 : * pgsql_tmp directory
696 : */
697 : Datum
698 0 : pg_ls_tmpdir_noargs(PG_FUNCTION_ARGS)
699 : {
700 0 : return pg_ls_tmpdir(fcinfo, DEFAULTTABLESPACE_OID);
701 : }
702 :
703 : /*
704 : * Function to return the list of temporary files in the specified tablespace's
705 : * pgsql_tmp directory
706 : */
707 : Datum
708 0 : pg_ls_tmpdir_1arg(PG_FUNCTION_ARGS)
709 : {
710 0 : return pg_ls_tmpdir(fcinfo, PG_GETARG_OID(0));
711 : }
712 :
713 : /*
714 : * Function to return the list of files in the WAL archive status directory.
715 : */
716 : Datum
717 4 : pg_ls_archive_statusdir(PG_FUNCTION_ARGS)
718 : {
719 4 : return pg_ls_dir_files(fcinfo, XLOGDIR "/archive_status", true);
720 : }
|