1 /* vi: set sw=4 ts=4: */
2 /*
3  * xreadlink.c - safe implementation of readlink.
4  * Returns a NULL on failure.
5  *
6  * Licensed under GPLv2, see file LICENSE in this source tree.
7  */
8 
9 #include "libbb.h"
10 
11 /* Some systems (eg Hurd) do not have MAXSYMLINKS definition,
12  * set it to some reasonable value if it isn't defined */
13 #ifndef MAXSYMLINKS
14 # define MAXSYMLINKS 20
15 #endif
16 
17 /*
18  * NOTE: This function returns a malloced char* that you will have to free
19  * yourself.
20  */
xmalloc_readlink(const char * path)21 char* FAST_FUNC xmalloc_readlink(const char *path)
22 {
23 	enum { GROWBY = 80 }; /* how large we will grow strings by */
24 
25 	char *buf = NULL;
26 	int bufsize = 0, readsize = 0;
27 
28 	do {
29 		bufsize += GROWBY;
30 		buf = xrealloc(buf, bufsize);
31 		readsize = readlink(path, buf, bufsize);
32 		if (readsize == -1) {
33 			free(buf);
34 			return NULL;
35 		}
36 	} while (bufsize < readsize + 1);
37 
38 	buf[readsize] = '\0';
39 
40 	return buf;
41 }
42 
43 /*
44  * This routine is not the same as realpath(), which
45  * canonicalizes the given path completely. This routine only
46  * follows trailing symlinks until a real file is reached and
47  * returns its name. If the path ends in a dangling link or if
48  * the target doesn't exist, the path is returned in any case.
49  * Intermediate symlinks in the path are not expanded -- only
50  * those at the tail.
51  * A malloced char* is returned, which must be freed by the caller.
52  */
xmalloc_follow_symlinks(const char * path)53 char* FAST_FUNC xmalloc_follow_symlinks(const char *path)
54 {
55 	char *buf;
56 	char *lpc;
57 	char *linkpath;
58 	int bufsize;
59 	int looping = MAXSYMLINKS + 1;
60 
61 	buf = xstrdup(path);
62 	goto jump_in;
63 
64 	while (1) {
65 		linkpath = xmalloc_readlink(buf);
66 		if (!linkpath) {
67 			/* not a symlink, or doesn't exist */
68 			if (errno == EINVAL || errno == ENOENT)
69 				return buf;
70 			goto free_buf_ret_null;
71 		}
72 
73 		if (!--looping) {
74 			free(linkpath);
75  free_buf_ret_null:
76 			free(buf);
77 			return NULL;
78 		}
79 
80 		if (*linkpath != '/') {
81 			bufsize += strlen(linkpath);
82 			buf = xrealloc(buf, bufsize);
83 			lpc = bb_get_last_path_component_strip(buf);
84 			strcpy(lpc, linkpath);
85 			free(linkpath);
86 		} else {
87 			free(buf);
88 			buf = linkpath;
89  jump_in:
90 			bufsize = strlen(buf) + 1;
91 		}
92 	}
93 }
94 
xmalloc_readlink_or_warn(const char * path)95 char* FAST_FUNC xmalloc_readlink_or_warn(const char *path)
96 {
97 	char *buf = xmalloc_readlink(path);
98 	if (!buf) {
99 		/* EINVAL => "file: Invalid argument" => puzzled user */
100 		const char *errmsg = "not a symlink";
101 		int err = errno;
102 		if (err != EINVAL)
103 			errmsg = strerror(err);
104 		bb_error_msg("%s: cannot read link: %s", path, errmsg);
105 	}
106 	return buf;
107 }
108 
xmalloc_realpath(const char * path)109 char* FAST_FUNC xmalloc_realpath(const char *path)
110 {
111 /* NB: uclibc also defines __GLIBC__
112  * Therefore the test "if glibc, or uclibc >= 0.9.31" looks a bit weird:
113  */
114 #if defined(__GLIBC__) && \
115     (!defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31))
116 	/* glibc provides a non-standard extension */
117 	/* new: POSIX.1-2008 specifies this behavior as well */
118 	return realpath(path, NULL);
119 #else
120 	char buf[PATH_MAX+1];
121 
122 	/* on error returns NULL (xstrdup(NULL) == NULL) */
123 	return xstrdup(realpath(path, buf));
124 #endif
125 }
126