1570af302Sopenharmony_ci#include <stdlib.h> 2570af302Sopenharmony_ci#include <limits.h> 3570af302Sopenharmony_ci#include <errno.h> 4570af302Sopenharmony_ci#include <unistd.h> 5570af302Sopenharmony_ci#include <string.h> 6570af302Sopenharmony_ci 7570af302Sopenharmony_cistatic size_t slash_len(const char *s) 8570af302Sopenharmony_ci{ 9570af302Sopenharmony_ci const char *s0 = s; 10570af302Sopenharmony_ci while (*s == '/') s++; 11570af302Sopenharmony_ci return s-s0; 12570af302Sopenharmony_ci} 13570af302Sopenharmony_ci 14570af302Sopenharmony_cichar *realpath(const char *restrict filename, char *restrict resolved) 15570af302Sopenharmony_ci{ 16570af302Sopenharmony_ci char stack[PATH_MAX+1]; 17570af302Sopenharmony_ci char output[PATH_MAX]; 18570af302Sopenharmony_ci size_t p, q, l, l0, cnt=0, nup=0; 19570af302Sopenharmony_ci int check_dir=0; 20570af302Sopenharmony_ci 21570af302Sopenharmony_ci if (!filename) { 22570af302Sopenharmony_ci errno = EINVAL; 23570af302Sopenharmony_ci return 0; 24570af302Sopenharmony_ci } 25570af302Sopenharmony_ci l = strnlen(filename, sizeof stack); 26570af302Sopenharmony_ci if (!l) { 27570af302Sopenharmony_ci errno = ENOENT; 28570af302Sopenharmony_ci return 0; 29570af302Sopenharmony_ci } 30570af302Sopenharmony_ci if (l >= PATH_MAX) goto toolong; 31570af302Sopenharmony_ci p = sizeof stack - l - 1; 32570af302Sopenharmony_ci q = 0; 33570af302Sopenharmony_ci memcpy(stack+p, filename, l+1); 34570af302Sopenharmony_ci 35570af302Sopenharmony_ci /* Main loop. Each iteration pops the next part from stack of 36570af302Sopenharmony_ci * remaining path components and consumes any slashes that follow. 37570af302Sopenharmony_ci * If not a link, it's moved to output; if a link, contents are 38570af302Sopenharmony_ci * pushed to the stack. */ 39570af302Sopenharmony_cirestart: 40570af302Sopenharmony_ci for (; ; p+=slash_len(stack+p)) { 41570af302Sopenharmony_ci /* If stack starts with /, the whole component is / or // 42570af302Sopenharmony_ci * and the output state must be reset. */ 43570af302Sopenharmony_ci if (stack[p] == '/') { 44570af302Sopenharmony_ci check_dir=0; 45570af302Sopenharmony_ci nup=0; 46570af302Sopenharmony_ci q=0; 47570af302Sopenharmony_ci output[q++] = '/'; 48570af302Sopenharmony_ci p++; 49570af302Sopenharmony_ci /* Initial // is special. */ 50570af302Sopenharmony_ci if (stack[p] == '/' && stack[p+1] != '/') 51570af302Sopenharmony_ci output[q++] = '/'; 52570af302Sopenharmony_ci continue; 53570af302Sopenharmony_ci } 54570af302Sopenharmony_ci 55570af302Sopenharmony_ci char *z = __strchrnul(stack+p, '/'); 56570af302Sopenharmony_ci l0 = l = z-(stack+p); 57570af302Sopenharmony_ci 58570af302Sopenharmony_ci if (!l && !check_dir) break; 59570af302Sopenharmony_ci 60570af302Sopenharmony_ci /* Skip any . component but preserve check_dir status. */ 61570af302Sopenharmony_ci if (l==1 && stack[p]=='.') { 62570af302Sopenharmony_ci p += l; 63570af302Sopenharmony_ci continue; 64570af302Sopenharmony_ci } 65570af302Sopenharmony_ci 66570af302Sopenharmony_ci /* Copy next component onto output at least temporarily, to 67570af302Sopenharmony_ci * call readlink, but wait to advance output position until 68570af302Sopenharmony_ci * determining it's not a link. */ 69570af302Sopenharmony_ci if (q && output[q-1] != '/') { 70570af302Sopenharmony_ci if (!p) goto toolong; 71570af302Sopenharmony_ci stack[--p] = '/'; 72570af302Sopenharmony_ci l++; 73570af302Sopenharmony_ci } 74570af302Sopenharmony_ci if (q+l >= PATH_MAX) goto toolong; 75570af302Sopenharmony_ci memcpy(output+q, stack+p, l); 76570af302Sopenharmony_ci output[q+l] = 0; 77570af302Sopenharmony_ci p += l; 78570af302Sopenharmony_ci 79570af302Sopenharmony_ci int up = 0; 80570af302Sopenharmony_ci if (l0==2 && stack[p-2]=='.' && stack[p-1]=='.') { 81570af302Sopenharmony_ci up = 1; 82570af302Sopenharmony_ci /* Any non-.. path components we could cancel start 83570af302Sopenharmony_ci * after nup repetitions of the 3-byte string "../"; 84570af302Sopenharmony_ci * if there are none, accumulate .. components to 85570af302Sopenharmony_ci * later apply to cwd, if needed. */ 86570af302Sopenharmony_ci if (q <= 3*nup) { 87570af302Sopenharmony_ci nup++; 88570af302Sopenharmony_ci q += l; 89570af302Sopenharmony_ci continue; 90570af302Sopenharmony_ci } 91570af302Sopenharmony_ci /* When previous components are already known to be 92570af302Sopenharmony_ci * directories, processing .. can skip readlink. */ 93570af302Sopenharmony_ci if (!check_dir) goto skip_readlink; 94570af302Sopenharmony_ci } 95570af302Sopenharmony_ci ssize_t k = readlink(output, stack, p); 96570af302Sopenharmony_ci if (k==p) goto toolong; 97570af302Sopenharmony_ci if (!k) { 98570af302Sopenharmony_ci errno = ENOENT; 99570af302Sopenharmony_ci return 0; 100570af302Sopenharmony_ci } 101570af302Sopenharmony_ci if (k<0) { 102570af302Sopenharmony_ci if (errno != EINVAL) return 0; 103570af302Sopenharmony_ciskip_readlink: 104570af302Sopenharmony_ci check_dir = 0; 105570af302Sopenharmony_ci if (up) { 106570af302Sopenharmony_ci while(q && output[q-1]!='/') q--; 107570af302Sopenharmony_ci if (q>1 && (q>2 || output[0]!='/')) q--; 108570af302Sopenharmony_ci continue; 109570af302Sopenharmony_ci } 110570af302Sopenharmony_ci if (l0) q += l; 111570af302Sopenharmony_ci check_dir = stack[p]; 112570af302Sopenharmony_ci continue; 113570af302Sopenharmony_ci } 114570af302Sopenharmony_ci if (++cnt == SYMLOOP_MAX) { 115570af302Sopenharmony_ci errno = ELOOP; 116570af302Sopenharmony_ci return 0; 117570af302Sopenharmony_ci } 118570af302Sopenharmony_ci 119570af302Sopenharmony_ci /* If link contents end in /, strip any slashes already on 120570af302Sopenharmony_ci * stack to avoid /->// or //->/// or spurious toolong. */ 121570af302Sopenharmony_ci if (stack[k-1]=='/') while (stack[p]=='/') p++; 122570af302Sopenharmony_ci p -= k; 123570af302Sopenharmony_ci memmove(stack+p, stack, k); 124570af302Sopenharmony_ci 125570af302Sopenharmony_ci /* Skip the stack advancement in case we have a new 126570af302Sopenharmony_ci * absolute base path. */ 127570af302Sopenharmony_ci goto restart; 128570af302Sopenharmony_ci } 129570af302Sopenharmony_ci 130570af302Sopenharmony_ci output[q] = 0; 131570af302Sopenharmony_ci 132570af302Sopenharmony_ci if (output[0] != '/') { 133570af302Sopenharmony_ci if (!getcwd(stack, sizeof stack)) return 0; 134570af302Sopenharmony_ci l = strlen(stack); 135570af302Sopenharmony_ci /* Cancel any initial .. components. */ 136570af302Sopenharmony_ci p = 0; 137570af302Sopenharmony_ci while (nup--) { 138570af302Sopenharmony_ci while(l>1 && stack[l-1]!='/') l--; 139570af302Sopenharmony_ci if (l>1) l--; 140570af302Sopenharmony_ci p += 2; 141570af302Sopenharmony_ci if (p<q) p++; 142570af302Sopenharmony_ci } 143570af302Sopenharmony_ci if (q-p && stack[l-1]!='/') stack[l++] = '/'; 144570af302Sopenharmony_ci if (l + (q-p) + 1 >= PATH_MAX) goto toolong; 145570af302Sopenharmony_ci memmove(output + l, output + p, q - p + 1); 146570af302Sopenharmony_ci memcpy(output, stack, l); 147570af302Sopenharmony_ci q = l + q-p; 148570af302Sopenharmony_ci } 149570af302Sopenharmony_ci 150570af302Sopenharmony_ci if (resolved) return memcpy(resolved, output, q+1); 151570af302Sopenharmony_ci else return strdup(output); 152570af302Sopenharmony_ci 153570af302Sopenharmony_citoolong: 154570af302Sopenharmony_ci errno = ENAMETOOLONG; 155570af302Sopenharmony_ci return 0; 156570af302Sopenharmony_ci} 157