1 /*
2  * realpath.c -- canonicalize pathname by removing symlinks
3  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4  * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
5  *
6  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
7  */
8 
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12 
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <limits.h>				/* for PATH_MAX */
18 #include <sys/param.h>			/* for MAXPATHLEN */
19 #include <errno.h>
20 #include <stdlib.h>
21 
22 #include <sys/stat.h>			/* for S_IFLNK */
23 
24 
25 #ifndef PATH_MAX
26 #ifdef _POSIX_VERSION
27 #define PATH_MAX _POSIX_PATH_MAX
28 #else
29 #ifdef MAXPATHLEN
30 #define PATH_MAX MAXPATHLEN
31 #else
32 #define PATH_MAX 1024
33 #endif
34 #endif
35 #endif
36 
37 #define MAX_READLINKS 32
38 
realpath(const char * path,char got_path[])39 char *realpath(const char *path, char got_path[])
40 {
41 	char copy_path[PATH_MAX];
42 	char *max_path, *new_path, *allocated_path;
43 	size_t path_len;
44 	int readlinks = 0;
45 #ifdef S_IFLNK
46 	int link_len;
47 #endif
48 
49 	if (path == NULL) {
50 		__set_errno(EINVAL);
51 		return NULL;
52 	}
53 	if (*path == '\0') {
54 		__set_errno(ENOENT);
55 		return NULL;
56 	}
57 	/* Make a copy of the source path since we may need to modify it. */
58 	path_len = strlen(path);
59 	if (path_len >= PATH_MAX - 2) {
60 		__set_errno(ENAMETOOLONG);
61 		return NULL;
62 	}
63 	/* Copy so that path is at the end of copy_path[] */
64 	strcpy(copy_path + (PATH_MAX-1) - path_len, path);
65 	path = copy_path + (PATH_MAX-1) - path_len;
66 	allocated_path = got_path ? NULL : (got_path = malloc(PATH_MAX));
67 	max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
68 	new_path = got_path;
69 	if (*path != '/') {
70 		/* If it's a relative pathname use getcwd for starters. */
71 		if (!getcwd(new_path, PATH_MAX - 1))
72 			goto err;
73 		new_path += strlen(new_path);
74 		if (new_path[-1] != '/')
75 			*new_path++ = '/';
76 	} else {
77 		*new_path++ = '/';
78 		path++;
79 	}
80 	/* Expand each slash-separated pathname component. */
81 	while (*path != '\0') {
82 		/* Ignore stray "/". */
83 		if (*path == '/') {
84 			path++;
85 			continue;
86 		}
87 		if (*path == '.') {
88 			/* Ignore ".". */
89 			if (path[1] == '\0' || path[1] == '/') {
90 				path++;
91 				continue;
92 			}
93 			if (path[1] == '.') {
94 				if (path[2] == '\0' || path[2] == '/') {
95 					path += 2;
96 					/* Ignore ".." at root. */
97 					if (new_path == got_path + 1)
98 						continue;
99 					/* Handle ".." by backing up. */
100 					while ((--new_path)[-1] != '/');
101 					continue;
102 				}
103 			}
104 		}
105 		/* Safely copy the next pathname component. */
106 		while (*path != '\0' && *path != '/') {
107 			if (new_path > max_path) {
108 				__set_errno(ENAMETOOLONG);
109  err:
110 				free(allocated_path);
111 				return NULL;
112 			}
113 			*new_path++ = *path++;
114 		}
115 #ifdef S_IFLNK
116 		/* Protect against infinite loops. */
117 		if (readlinks++ > MAX_READLINKS) {
118 			__set_errno(ELOOP);
119 			goto err;
120 		}
121 		path_len = strlen(path);
122 		/* See if last (so far) pathname component is a symlink. */
123 		*new_path = '\0';
124 		{
125 			int sv_errno = errno;
126 			link_len = readlink(got_path, copy_path, PATH_MAX - 1);
127 			if (link_len < 0) {
128 				/* EINVAL means the file exists but isn't a symlink. */
129 				if (errno != EINVAL) {
130 					goto err;
131 				}
132 			} else {
133 				/* Safe sex check. */
134 				if (path_len + link_len >= PATH_MAX - 2) {
135 					__set_errno(ENAMETOOLONG);
136 					goto err;
137 				}
138 				/* Note: readlink doesn't add the null byte. */
139 				/* copy_path[link_len] = '\0'; - we don't need it too */
140 				if (*copy_path == '/')
141 					/* Start over for an absolute symlink. */
142 					new_path = got_path;
143 				else
144 					/* Otherwise back up over this component. */
145 					while (*(--new_path) != '/');
146 				/* Prepend symlink contents to path. */
147 				memmove(copy_path + (PATH_MAX-1) - link_len - path_len, copy_path, link_len);
148 				path = copy_path + (PATH_MAX-1) - link_len - path_len;
149 			}
150 			__set_errno(sv_errno);
151 		}
152 #endif							/* S_IFLNK */
153 		*new_path++ = '/';
154 	}
155 	/* Delete trailing slash but don't whomp a lone slash. */
156 	if (new_path != got_path + 1 && new_path[-1] == '/')
157 		new_path--;
158 	/* Make sure it's null terminated. */
159 	*new_path = '\0';
160 	return got_path;
161 }
162 libc_hidden_def(realpath)
163