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