summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libbb.h1
-rw-r--r--libbb/xreadlink.c45
2 files changed, 46 insertions, 0 deletions
diff --git a/include/libbb.h b/include/libbb.h
index f79d80d..0170085 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -260,6 +260,7 @@ DIR *warn_opendir(const char *path);
/* UNUSED: char *xmalloc_realpath(const char *path); */
char *xmalloc_readlink(const char *path);
+char *xmalloc_readlink_follow(const char *path);
char *xmalloc_readlink_or_warn(const char *path);
char *xrealloc_getcwd_or_warn(char *cwd);
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index 98b795f..2f6b1e2 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -32,6 +32,51 @@ char *xmalloc_readlink(const char *path)
return buf;
}
+/*
+ * this routine is not the same as realpath(), which canonicalizes
+ * the given path completely. this routine only follows trailing
+ * symlinks until a real file is reached, and returns its name.
+ * intermediate symlinks are not expanded. as above, a malloced
+ * char* is returned, which must be freed.
+ */
+char *xmalloc_readlink_follow(const char *path)
+{
+ char *buf = NULL, *lpc, *linkpath;
+ int bufsize;
+ smallint looping = 0;
+
+ buf = strdup(path);
+ bufsize = strlen(path) + 1;
+
+ while(1) {
+ linkpath = xmalloc_readlink(buf);
+ if (!linkpath) {
+ if (errno == EINVAL) /* not a symlink */
+ return buf;
+ free(buf);
+ return NULL;
+ }
+
+ if (*linkpath == '/') {
+ free(buf);
+ buf = linkpath;
+ bufsize = strlen(linkpath) + 1;
+ } else {
+ bufsize += strlen(linkpath);
+ if (looping++ > MAXSYMLINKS) {
+ free(linkpath);
+ free(buf);
+ return NULL;
+ }
+ buf = xrealloc(buf, bufsize);
+ lpc = bb_get_last_path_component_strip(buf);
+ strcpy(lpc, linkpath);
+ free(linkpath);
+ }
+ }
+
+}
+
char *xmalloc_readlink_or_warn(const char *path)
{
char *buf = xmalloc_readlink(path);