• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdlib.h>
2 #include <limits.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <string.h>
6 
slash_len(const char * s)7 static size_t slash_len(const char *s)
8 {
9 	const char *s0 = s;
10 	while (*s == '/') s++;
11 	return s-s0;
12 }
13 
realpath(const char * restrict filename,char * restrict resolved)14 char *realpath(const char *restrict filename, char *restrict resolved)
15 {
16 	char stack[PATH_MAX+1];
17 	char output[PATH_MAX];
18 	size_t p, q, l, l0, cnt=0, nup=0;
19 	int check_dir=0;
20 
21 	if (!filename) {
22 		errno = EINVAL;
23 		return 0;
24 	}
25 	l = strnlen(filename, sizeof stack);
26 	if (!l) {
27 		errno = ENOENT;
28 		return 0;
29 	}
30 	if (l >= PATH_MAX) goto toolong;
31 	p = sizeof stack - l - 1;
32 	q = 0;
33 	memcpy(stack+p, filename, l+1);
34 
35 	/* Main loop. Each iteration pops the next part from stack of
36 	 * remaining path components and consumes any slashes that follow.
37 	 * If not a link, it's moved to output; if a link, contents are
38 	 * pushed to the stack. */
39 restart:
40 	for (; ; p+=slash_len(stack+p)) {
41 		/* If stack starts with /, the whole component is / or //
42 		 * and the output state must be reset. */
43 		if (stack[p] == '/') {
44 			check_dir=0;
45 			nup=0;
46 			q=0;
47 			output[q++] = '/';
48 			p++;
49 			/* Initial // is special. */
50 			if (stack[p] == '/' && stack[p+1] != '/')
51 				output[q++] = '/';
52 			continue;
53 		}
54 
55 		char *z = __strchrnul(stack+p, '/');
56 		l0 = l = z-(stack+p);
57 
58 		if (!l && !check_dir) break;
59 
60 		/* Skip any . component but preserve check_dir status. */
61 		if (l==1 && stack[p]=='.') {
62 			p += l;
63 			continue;
64 		}
65 
66 		/* Copy next component onto output at least temporarily, to
67 		 * call readlink, but wait to advance output position until
68 		 * determining it's not a link. */
69 		if (q && output[q-1] != '/') {
70 			if (!p) goto toolong;
71 			stack[--p] = '/';
72 			l++;
73 		}
74 		if (q+l >= PATH_MAX) goto toolong;
75 		memcpy(output+q, stack+p, l);
76 		output[q+l] = 0;
77 		p += l;
78 
79 		int up = 0;
80 		if (l0==2 && stack[p-2]=='.' && stack[p-1]=='.') {
81 			up = 1;
82 			/* Any non-.. path components we could cancel start
83 			 * after nup repetitions of the 3-byte string "../";
84 			 * if there are none, accumulate .. components to
85 			 * later apply to cwd, if needed. */
86 			if (q <= 3*nup) {
87 				nup++;
88 				q += l;
89 				continue;
90 			}
91 			/* When previous components are already known to be
92 			 * directories, processing .. can skip readlink. */
93 			if (!check_dir) goto skip_readlink;
94 		}
95 		ssize_t k = readlink(output, stack, p);
96 		if (k==p) goto toolong;
97 		if (!k) {
98 			errno = ENOENT;
99 			return 0;
100 		}
101 		if (k<0) {
102 			if (errno != EINVAL) return 0;
103 skip_readlink:
104 			check_dir = 0;
105 			if (up) {
106 				while(q && output[q-1]!='/') q--;
107 				if (q>1 && (q>2 || output[0]!='/')) q--;
108 				continue;
109 			}
110 			if (l0) q += l;
111 			check_dir = stack[p];
112 			continue;
113 		}
114 		if (++cnt == SYMLOOP_MAX) {
115 			errno = ELOOP;
116 			return 0;
117 		}
118 
119 		/* If link contents end in /, strip any slashes already on
120 		 * stack to avoid /->// or //->/// or spurious toolong. */
121 		if (stack[k-1]=='/') while (stack[p]=='/') p++;
122 		p -= k;
123 		memmove(stack+p, stack, k);
124 
125 		/* Skip the stack advancement in case we have a new
126 		 * absolute base path. */
127 		goto restart;
128 	}
129 
130  	output[q] = 0;
131 
132 	if (output[0] != '/') {
133 		if (!getcwd(stack, sizeof stack)) return 0;
134 		l = strlen(stack);
135 		/* Cancel any initial .. components. */
136 		p = 0;
137 		while (nup--) {
138 			while(l>1 && stack[l-1]!='/') l--;
139 			if (l>1) l--;
140 			p += 2;
141 			if (p<q) p++;
142 		}
143 		if (q-p && stack[l-1]!='/') stack[l++] = '/';
144 		if (l + (q-p) + 1 >= PATH_MAX) goto toolong;
145 		memmove(output + l, output + p, q - p + 1);
146 		memcpy(output, stack, l);
147 		q = l + q-p;
148 	}
149 
150 	if (resolved) return memcpy(resolved, output, q+1);
151 	else return strdup(output);
152 
153 toolong:
154 	errno = ENAMETOOLONG;
155 	return 0;
156 }
157