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