blob: cf9d45fa26b364297000e4ace2940f6338b6682b [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
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
39char *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}
162libc_hidden_def(realpath)