OSDN Git Service

e9eabdfaa8bbee71ca2aace3b1ad2ce23cbae141
[uclinux-h8/uClibc.git] / libc / stdlib / realpath.c
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 /* Experimentally off - libc_hidden_proto(strcat) */
25 /* Experimentally off - libc_hidden_proto(strcpy) */
26 /* Experimentally off - libc_hidden_proto(strlen) */
27 libc_hidden_proto(readlink)
28 libc_hidden_proto(getcwd)
29
30 #ifndef PATH_MAX
31 #ifdef _POSIX_VERSION
32 #define PATH_MAX _POSIX_PATH_MAX
33 #else
34 #ifdef MAXPATHLEN
35 #define PATH_MAX MAXPATHLEN
36 #else
37 #define PATH_MAX 1024
38 #endif
39 #endif
40 #endif
41
42 #define MAX_READLINKS 32
43
44 #ifdef __STDC__
45 char *realpath(const char *path, char got_path[])
46 #else
47 char *realpath(path, got_path)
48 const char *path;
49 char got_path[];
50 #endif
51 {
52         char copy_path[PATH_MAX];
53         /* use user supplied buffer directly - reduces stack usage */
54         /* char got_path[PATH_MAX]; */
55         char *max_path;
56         char *new_path;
57         size_t path_len;
58         int readlinks = 0;
59 #ifdef S_IFLNK
60         int link_len;
61 #endif
62
63         if (path == NULL) {
64                 __set_errno(EINVAL);
65                 return NULL;
66         }
67         if (*path == '\0') {
68                 __set_errno(ENOENT);
69                 return NULL;
70         }
71         /* Make a copy of the source path since we may need to modify it. */
72         path_len = strlen(path);
73         if (path_len >= PATH_MAX - 2) {
74                 __set_errno(ENAMETOOLONG);
75                 return NULL;
76         }
77         /* Copy so that path is at the end of copy_path[] */
78         strcpy(copy_path + (PATH_MAX-1) - path_len, path);
79         path = copy_path + (PATH_MAX-1) - path_len;
80         max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
81         new_path = got_path;
82         if (*path != '/') {
83                 /* If it's a relative pathname use getcwd for starters. */
84                 if (!getcwd(new_path, PATH_MAX - 1))
85                         return NULL;
86                 new_path += strlen(new_path);
87                 if (new_path[-1] != '/')
88                         *new_path++ = '/';
89         } else {
90                 *new_path++ = '/';
91                 path++;
92         }
93         /* Expand each slash-separated pathname component. */
94         while (*path != '\0') {
95                 /* Ignore stray "/". */
96                 if (*path == '/') {
97                         path++;
98                         continue;
99                 }
100                 if (*path == '.') {
101                         /* Ignore ".". */
102                         if (path[1] == '\0' || path[1] == '/') {
103                                 path++;
104                                 continue;
105                         }
106                         if (path[1] == '.') {
107                                 if (path[2] == '\0' || path[2] == '/') {
108                                         path += 2;
109                                         /* Ignore ".." at root. */
110                                         if (new_path == got_path + 1)
111                                                 continue;
112                                         /* Handle ".." by backing up. */
113                                         while ((--new_path)[-1] != '/');
114                                         continue;
115                                 }
116                         }
117                 }
118                 /* Safely copy the next pathname component. */
119                 while (*path != '\0' && *path != '/') {
120                         if (new_path > max_path) {
121                                 __set_errno(ENAMETOOLONG);
122                                 return NULL;
123                         }
124                         *new_path++ = *path++;
125                 }
126 #ifdef S_IFLNK
127                 /* Protect against infinite loops. */
128                 if (readlinks++ > MAX_READLINKS) {
129                         __set_errno(ELOOP);
130                         return NULL;
131                 }
132                 path_len = strlen(path);
133                 /* See if last (so far) pathname component is a symlink. */
134                 *new_path = '\0';
135                 {
136                         int sv_errno = errno;
137                         link_len = readlink(got_path, copy_path, PATH_MAX - 1);
138                         if (link_len < 0) {
139                                 /* EINVAL means the file exists but isn't a symlink. */
140                                 if (errno != EINVAL) {
141                                         return NULL;
142                                 }
143                         } else {
144                                 /* Safe sex check. */
145                                 if (path_len + link_len >= PATH_MAX - 2) {
146                                         __set_errno(ENAMETOOLONG);
147                                         return NULL;
148                                 }
149                                 /* Note: readlink doesn't add the null byte. */
150                                 /* copy_path[link_len] = '\0'; - we don't need it too */
151                                 if (*copy_path == '/')
152                                         /* Start over for an absolute symlink. */
153                                         new_path = got_path;
154                                 else
155                                         /* Otherwise back up over this component. */
156                                         while (*(--new_path) != '/');
157                                 /* Prepend symlink contents to path. */
158                                 memmove(copy_path + (PATH_MAX-1) - link_len - path_len, copy_path, link_len);
159                                 path = copy_path + (PATH_MAX-1) - link_len - path_len;
160                         }
161                         __set_errno(sv_errno);
162                 }
163 #endif                                                  /* S_IFLNK */
164                 *new_path++ = '/';
165         }
166         /* Delete trailing slash but don't whomp a lone slash. */
167         if (new_path != got_path + 1 && new_path[-1] == '/')
168                 new_path--;
169         /* Make sure it's null terminated. */
170         *new_path = '\0';
171         return got_path;
172 }