OSDN Git Service

Hide more
[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  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Library Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Library Public License for more details.
14  */
15
16 #define readlink __readlink
17 #define getcwd __getcwd
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <limits.h>                             /* for PATH_MAX */
29 #include <sys/param.h>                  /* for MAXPATHLEN */
30 #include <errno.h>
31
32 #include <sys/stat.h>                   /* for S_IFLNK */
33
34 #ifndef PATH_MAX
35 #ifdef _POSIX_VERSION
36 #define PATH_MAX _POSIX_PATH_MAX
37 #else
38 #ifdef MAXPATHLEN
39 #define PATH_MAX MAXPATHLEN
40 #else
41 #define PATH_MAX 1024
42 #endif
43 #endif
44 #endif
45
46 #define MAX_READLINKS 32
47
48 #ifdef __STDC__
49 char *realpath(const char *path, char resolved_path[])
50 #else
51 char *realpath(path, resolved_path)
52 const char *path;
53 char resolved_path[];
54 #endif
55 {
56         char copy_path[PATH_MAX];
57         char link_path[PATH_MAX];
58         char got_path[PATH_MAX];
59         char *new_path = got_path;
60         char *max_path;
61         int readlinks = 0;
62         int n;
63
64         /* Make a copy of the source path since we may need to modify it. */
65         if (__strlen(path) >= PATH_MAX - 2) {
66                 __set_errno(ENAMETOOLONG);
67                 return NULL;
68         }
69         __strcpy(copy_path, path);
70         path = copy_path;
71         max_path = copy_path + PATH_MAX - 2;
72         /* If it's a relative pathname use getwd for starters. */
73         if (*path != '/') {
74                 /* Ohoo... */
75 #define HAVE_GETCWD
76 #ifdef HAVE_GETCWD
77                 getcwd(new_path, PATH_MAX - 1);
78 #else
79                 getwd(new_path);
80 #endif
81                 new_path += __strlen(new_path);
82                 if (new_path[-1] != '/')
83                         *new_path++ = '/';
84         } else {
85                 *new_path++ = '/';
86                 path++;
87         }
88         /* Expand each slash-separated pathname component. */
89         while (*path != '\0') {
90                 /* Ignore stray "/". */
91                 if (*path == '/') {
92                         path++;
93                         continue;
94                 }
95                 if (*path == '.') {
96                         /* Ignore ".". */
97                         if (path[1] == '\0' || path[1] == '/') {
98                                 path++;
99                                 continue;
100                         }
101                         if (path[1] == '.') {
102                                 if (path[2] == '\0' || path[2] == '/') {
103                                         path += 2;
104                                         /* Ignore ".." at root. */
105                                         if (new_path == got_path + 1)
106                                                 continue;
107                                         /* Handle ".." by backing up. */
108                                         while ((--new_path)[-1] != '/');
109                                         continue;
110                                 }
111                         }
112                 }
113                 /* Safely copy the next pathname component. */
114                 while (*path != '\0' && *path != '/') {
115                         if (path > max_path) {
116                                 __set_errno(ENAMETOOLONG);
117                                 return NULL;
118                         }
119                         *new_path++ = *path++;
120                 }
121 #ifdef S_IFLNK
122                 /* Protect against infinite loops. */
123                 if (readlinks++ > MAX_READLINKS) {
124                         __set_errno(ELOOP);
125                         return NULL;
126                 }
127                 /* See if latest pathname component is a symlink. */
128                 *new_path = '\0';
129                 n = readlink(got_path, link_path, PATH_MAX - 1);
130                 if (n < 0) {
131                         /* EINVAL means the file exists but isn't a symlink. */
132                         if (errno != EINVAL) {
133                                 /* Make sure it's null terminated. */
134                                 *new_path = '\0';
135                                 __strcpy(resolved_path, got_path);
136                                 return NULL;
137                         }
138                 } else {
139                         /* Note: readlink doesn't add the null byte. */
140                         link_path[n] = '\0';
141                         if (*link_path == '/')
142                                 /* Start over for an absolute symlink. */
143                                 new_path = got_path;
144                         else
145                                 /* Otherwise back up over this component. */
146                                 while (*(--new_path) != '/');
147                         /* Safe sex check. */
148                         if (__strlen(path) + n >= PATH_MAX - 2) {
149                                 __set_errno(ENAMETOOLONG);
150                                 return NULL;
151                         }
152                         /* Insert symlink contents into path. */
153                         __strcat(link_path, path);
154                         __strcpy(copy_path, link_path);
155                         path = copy_path;
156                 }
157 #endif                                                  /* S_IFLNK */
158                 *new_path++ = '/';
159         }
160         /* Delete trailing slash but don't whomp a lone slash. */
161         if (new_path != got_path + 1 && new_path[-1] == '/')
162                 new_path--;
163         /* Make sure it's null terminated. */
164         *new_path = '\0';
165         __strcpy(resolved_path, got_path);
166         return resolved_path;
167 }