source: rtems/cpukit/libcsupport/src/realpath.c @ 1c6926c1

5
Last change on this file since 1c6926c1 was ea9232d, checked in by Chris Johns <chrisj@…>, on 10/24/14 at 23:05:59

ilibcsupport: Add realpath call.

  • Property mode set to 100644
File size: 6.5 KB
Line 
1/*
2 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The names of the authors may not be used to endorse or promote
13 *    products derived from this software without specific prior written
14 *    permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#if defined(LIBC_SCCS) && !defined(lint)
30static char sccsid[] = "@(#)realpath.c  8.1 (Berkeley) 2/16/94";
31#endif /* LIBC_SCCS and not lint */
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: release/9.1.0/lib/libc/stdlib/realpath.c 240647 2012-09-18 13:03:00Z emaste $");
34
35#if !defined(__rtems__)
36#include "namespace.h"
37#endif
38#include <sys/param.h>
39#include <sys/stat.h>
40
41#include <errno.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45#if !defined(__rtems__)
46#include "un-namespace.h"
47#endif
48
49/*
50 * Find the real name of path, by removing all ".", ".." and symlink
51 * components.  Returns (resolved) on success, or (NULL) on failure,
52 * in which case the path which caused trouble is left in (resolved).
53 */
54char *
55realpath(const char * __restrict path, char * __restrict resolved)
56{
57        struct stat sb;
58        char *p, *q, *s;
59        size_t left_len, resolved_len;
60        unsigned symlinks;
61        int m, slen;
62        char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
63
64        if (path == NULL) {
65                errno = EINVAL;
66                return (NULL);
67        }
68        if (path[0] == '\0') {
69                errno = ENOENT;
70                return (NULL);
71        }
72        if (resolved == NULL) {
73                resolved = malloc(PATH_MAX);
74                if (resolved == NULL)
75                        return (NULL);
76                m = 1;
77        } else
78                m = 0;
79        symlinks = 0;
80        if (path[0] == '/') {
81                resolved[0] = '/';
82                resolved[1] = '\0';
83                if (path[1] == '\0')
84                        return (resolved);
85                resolved_len = 1;
86                left_len = strlcpy(left, path + 1, sizeof(left));
87        } else {
88                if (getcwd(resolved, PATH_MAX) == NULL) {
89                        if (m)
90                                free(resolved);
91                        else {
92                                resolved[0] = '.';
93                                resolved[1] = '\0';
94                        }
95                        return (NULL);
96                }
97                resolved_len = strlen(resolved);
98                left_len = strlcpy(left, path, sizeof(left));
99        }
100        if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
101                if (m)
102                        free(resolved);
103                errno = ENAMETOOLONG;
104                return (NULL);
105        }
106
107        /*
108         * Iterate over path components in `left'.
109         */
110        while (left_len != 0) {
111                /*
112                 * Extract the next path component and adjust `left'
113                 * and its length.
114                 */
115                p = strchr(left, '/');
116                s = p ? p : left + left_len;
117                if (s - left >= sizeof(next_token)) {
118                        if (m)
119                                free(resolved);
120                        errno = ENAMETOOLONG;
121                        return (NULL);
122                }
123                memcpy(next_token, left, s - left);
124                next_token[s - left] = '\0';
125                left_len -= s - left;
126                if (p != NULL)
127                        memmove(left, s + 1, left_len + 1);
128                if (resolved[resolved_len - 1] != '/') {
129                        if (resolved_len + 1 >= PATH_MAX) {
130                                if (m)
131                                        free(resolved);
132                                errno = ENAMETOOLONG;
133                                return (NULL);
134                        }
135                        resolved[resolved_len++] = '/';
136                        resolved[resolved_len] = '\0';
137                }
138                if (next_token[0] == '\0') {
139                        /*
140                         * Handle consequential slashes.  The path
141                         * before slash shall point to a directory.
142                         *
143                         * Only the trailing slashes are not covered
144                         * by other checks in the loop, but we verify
145                         * the prefix for any (rare) "//" or "/\0"
146                         * occurence to not implement lookahead.
147                         */
148                        if (lstat(resolved, &sb) != 0) {
149                                if (m)
150                                        free(resolved);
151                                return (NULL);
152                        }
153                        if (!S_ISDIR(sb.st_mode)) {
154                                if (m)
155                                        free(resolved);
156                                errno = ENOTDIR;
157                                return (NULL);
158                        }
159                        continue;
160                }
161                else if (strcmp(next_token, ".") == 0)
162                        continue;
163                else if (strcmp(next_token, "..") == 0) {
164                        /*
165                         * Strip the last path component except when we have
166                         * single "/"
167                         */
168                        if (resolved_len > 1) {
169                                resolved[resolved_len - 1] = '\0';
170                                q = strrchr(resolved, '/') + 1;
171                                *q = '\0';
172                                resolved_len = q - resolved;
173                        }
174                        continue;
175                }
176
177                /*
178                 * Append the next path component and lstat() it.
179                 */
180                resolved_len = strlcat(resolved, next_token, PATH_MAX);
181                if (resolved_len >= PATH_MAX) {
182                        if (m)
183                                free(resolved);
184                        errno = ENAMETOOLONG;
185                        return (NULL);
186                }
187                if (lstat(resolved, &sb) != 0) {
188                        if (m)
189                                free(resolved);
190                        return (NULL);
191                }
192                if (S_ISLNK(sb.st_mode)) {
193                        if (symlinks++ > MAXSYMLINKS) {
194                                if (m)
195                                        free(resolved);
196                                errno = ELOOP;
197                                return (NULL);
198                        }
199                        slen = readlink(resolved, symlink, sizeof(symlink) - 1);
200                        if (slen < 0) {
201                                if (m)
202                                        free(resolved);
203                                return (NULL);
204                        }
205                        symlink[slen] = '\0';
206                        if (symlink[0] == '/') {
207                                resolved[1] = 0;
208                                resolved_len = 1;
209                        } else if (resolved_len > 1) {
210                                /* Strip the last path component. */
211                                resolved[resolved_len - 1] = '\0';
212                                q = strrchr(resolved, '/') + 1;
213                                *q = '\0';
214                                resolved_len = q - resolved;
215                        }
216
217                        /*
218                         * If there are any path components left, then
219                         * append them to symlink. The result is placed
220                         * in `left'.
221                         */
222                        if (p != NULL) {
223                                if (symlink[slen - 1] != '/') {
224                                        if (slen + 1 >= sizeof(symlink)) {
225                                                if (m)
226                                                        free(resolved);
227                                                errno = ENAMETOOLONG;
228                                                return (NULL);
229                                        }
230                                        symlink[slen] = '/';
231                                        symlink[slen + 1] = 0;
232                                }
233                                left_len = strlcat(symlink, left,
234                                    sizeof(symlink));
235                                if (left_len >= sizeof(left)) {
236                                        if (m)
237                                                free(resolved);
238                                        errno = ENAMETOOLONG;
239                                        return (NULL);
240                                }
241                        }
242                        left_len = strlcpy(left, symlink, sizeof(left));
243                }
244        }
245
246        /*
247         * Remove trailing slash except when the resolved pathname
248         * is a single "/".
249         */
250        if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
251                resolved[resolved_len - 1] = '\0';
252        return (resolved);
253}
Note: See TracBrowser for help on using the repository browser.