From 0ea59fb774e61e60def14f5a3470429695ad6adc Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 29 Jun 2015 14:40:20 +0900 Subject: [PATCH 3/5] 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 | 66 +++++++++++++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.h | 4 +++ src/include/utils/builtins.h | 2 ++ 4 files changed, 81 insertions(+) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 67e1626..80a4f26 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17845,6 +17845,15 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); Return information about a file. + + + pg_readlink(filename text[, missing_ok boolean]) + + text + + Return value of a symbolic link. + + diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index df8ae88..a0f8d7b 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -480,3 +480,69 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS) { return pg_ls_dir(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 missing_ok = false; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to read files")))); + + if (PG_NARGS() == 2) + missing_ok = 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 (missing_ok && 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("symbolic links are not supported by this platform"))); + PG_RETURN_NULL(); +#endif +} + +/* + * pg_readlink (1 argument version) + * + * note: this wrapper is necessary to pass the sanity check in opr_sanity, + * which checks that all built-in functions that share the implementing C + * function take the same number of arguments + */ +Datum +pg_readlink_1arg(PG_FUNCTION_ARGS) +{ + return pg_readlink(fcinfo); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f6abd74..f9c6176 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3200,6 +3200,10 @@ 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 _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 v 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ pg_readlink_1arg _null_ _null_ _null_ )); +DESCR("read value of a symbolic link"); +DATA(insert OID = 3299 ( pg_readlink PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 25 "25 16" _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 9855672..615e5ab 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -479,6 +479,8 @@ extern Datum pg_read_binary_file_off_len(PG_FUNCTION_ARGS); extern Datum pg_read_binary_file_all(PG_FUNCTION_ARGS); extern Datum pg_ls_dir(PG_FUNCTION_ARGS); extern Datum pg_ls_dir_1arg(PG_FUNCTION_ARGS); +extern Datum pg_readlink(PG_FUNCTION_ARGS); +extern Datum pg_readlink_1arg(PG_FUNCTION_ARGS); /* misc.c */ extern Datum current_database(PG_FUNCTION_ARGS); -- 2.4.5