From ef78401e6b136577fb1703a6820743ab404631b1 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Wed, 24 Jun 2015 13:03:08 +0900
Subject: [PATCH 6/8] Add pg_readlink to get value of a symbolic link

This is similar to Posix's readlink, except that PostgreSQL restrictions
are applied to the source path queried, similarly to other functions of
genfile.c.
---
 doc/src/sgml/func.sgml          |  9 +++++++
 src/backend/utils/adt/genfile.c | 54 +++++++++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.h   |  2 ++
 src/include/utils/builtins.h    |  1 +
 4 files changed, 66 insertions(+)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 20b91ab..81a8090 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17855,6 +17855,15 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         an error.
        </entry>
       </row>
+      <row>
+       <entry>
+        <literal><function>pg_readlink(<parameter>filename</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Return value of a symbolic link.
+       </entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 456061b..94ff7d6 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -585,3 +585,57 @@ pg_ls_dir_extended(PG_FUNCTION_ARGS)
 {
 	return ls_dir_wrapper(fcinfo);
 }
+
+
+/*
+ * pg_readlink - Find the real location of the provided symbolic link.
+ */
+Datum
+pg_readlink(PG_FUNCTION_ARGS)
+{
+	text	   *sourcepath_t = PG_GETARG_TEXT_P(0);
+	char	   *sourcepath;
+	char        targetpath[MAXPGPATH];
+	int			rllen;
+	bool		if_not_exists = false;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to read files"))));
+
+	/* Check for option if_not_exists */
+	if (PG_NARGS() == 2 && !PG_ARGISNULL(1))
+		if_not_exists = PG_GETARG_BOOL(1);
+
+#if defined(HAVE_READLINK) || defined(WIN32)
+	sourcepath = convert_and_check_filename(sourcepath_t);
+
+	rllen = readlink(sourcepath, targetpath, sizeof(targetpath));
+	if (rllen < 0)
+	{
+		if (if_not_exists && errno == ENOENT)
+			PG_RETURN_NULL();
+		else
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read symbolic link \"%s\": %m",
+							sourcepath)));
+	}
+	if (rllen >= sizeof(targetpath))
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("symbolic link \"%s\" target is too long",
+						sourcepath)));
+	}
+	targetpath[rllen] = '\0';
+
+	PG_RETURN_TEXT_P(cstring_to_text(targetpath));
+#else
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("readline is not available on this platform")));
+	PG_RETURN_NULL();
+#endif
+}
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 36d3e15..11dd80d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3207,6 +3207,8 @@ DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 0 f f f f t t v 1 0
 DESCR("list all files in a directory");
 DATA(insert OID = 3297 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 0 f f f f t t v 3 0 25 "25 16 16" _null_ _null_ _null_ _null_ _null_ pg_ls_dir_extended _null_ _null_ _null_ ));
 DESCR("list all files in a directory");
+DATA(insert OID = 3298 ( pg_readlink		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ pg_readlink _null_ _null_ _null_ ));
+DESCR("read value of a symbolic link");
 DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
 DESCR("sleep for the specified time in seconds");
 DATA(insert OID = 3935 ( pg_sleep_for			PGNSP PGUID 14 1 0 0 0 f f f f t f v 1 0 2278 "1186" _null_ _null_ _null_ _null_ _null_ "select pg_catalog.pg_sleep(extract(epoch from pg_catalog.clock_timestamp() operator(pg_catalog.+) $1) operator(pg_catalog.-) extract(epoch from pg_catalog.clock_timestamp()))" _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 446cda6..27a4fd8 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -485,6 +485,7 @@ extern Datum pg_read_binary_file_all(PG_FUNCTION_ARGS);
 extern Datum pg_read_binary_file_all_extended(PG_FUNCTION_ARGS);
 extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
 extern Datum pg_ls_dir_extended(PG_FUNCTION_ARGS);
+extern Datum pg_readlink(PG_FUNCTION_ARGS);
 
 /* misc.c */
 extern Datum current_database(PG_FUNCTION_ARGS);
-- 
2.4.4

