Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:15

0001 /*
0002  * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
0003  *
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  * 1. Redistributions of source code must retain the above copyright
0008  *    notice, this list of conditions and the following disclaimer.
0009  * 2. Redistributions in binary form must reproduce the above copyright
0010  *    notice, this list of conditions and the following disclaimer in the
0011  *    documentation and/or other materials provided with the distribution.
0012  * 3. The names of the authors may not be used to endorse or promote
0013  *    products derived from this software without specific prior written
0014  *    permission.
0015  *
0016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0019  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0020  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0021  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0022  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0023  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0024  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0025  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0026  * SUCH DAMAGE.
0027  */
0028 
0029 #if defined(LIBC_SCCS) && !defined(lint)
0030 static char sccsid[] = "@(#)realpath.c  8.1 (Berkeley) 2/16/94";
0031 #endif /* LIBC_SCCS and not lint */
0032 #include <sys/cdefs.h>
0033 __FBSDID("$FreeBSD: release/9.1.0/lib/libc/stdlib/realpath.c 240647 2012-09-18 13:03:00Z emaste $");
0034 
0035 #if !defined(__rtems__)
0036 #include "namespace.h"
0037 #endif
0038 #include <sys/param.h>
0039 #include <sys/stat.h>
0040 
0041 #include <errno.h>
0042 #include <stdlib.h>
0043 #include <string.h>
0044 #include <unistd.h>
0045 #if !defined(__rtems__)
0046 #include "un-namespace.h"
0047 #endif
0048 
0049 /*
0050  * Find the real name of path, by removing all ".", ".." and symlink
0051  * components.  Returns (resolved) on success, or (NULL) on failure,
0052  * in which case the path which caused trouble is left in (resolved).
0053  */
0054 char *
0055 realpath(const char * __restrict path, char * __restrict resolved)
0056 {
0057     struct stat sb;
0058     char *p, *q, *s;
0059     size_t left_len, resolved_len;
0060     unsigned symlinks;
0061     int m, slen;
0062     char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
0063 
0064     if (path == NULL) {
0065         errno = EINVAL;
0066         return (NULL);
0067     }
0068     if (path[0] == '\0') {
0069         errno = ENOENT;
0070         return (NULL);
0071     }
0072     if (resolved == NULL) {
0073         resolved = malloc(PATH_MAX);
0074         if (resolved == NULL)
0075             return (NULL);
0076         m = 1;
0077     } else
0078         m = 0;
0079     symlinks = 0;
0080     if (path[0] == '/') {
0081         resolved[0] = '/';
0082         resolved[1] = '\0';
0083         if (path[1] == '\0')
0084             return (resolved);
0085         resolved_len = 1;
0086         left_len = strlcpy(left, path + 1, sizeof(left));
0087     } else {
0088         if (getcwd(resolved, PATH_MAX) == NULL) {
0089             if (m)
0090                 free(resolved);
0091             else {
0092                 resolved[0] = '.';
0093                 resolved[1] = '\0';
0094             }
0095             return (NULL);
0096         }
0097         resolved_len = strlen(resolved);
0098         left_len = strlcpy(left, path, sizeof(left));
0099     }
0100     if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
0101         if (m)
0102             free(resolved);
0103         errno = ENAMETOOLONG;
0104         return (NULL);
0105     }
0106 
0107     /*
0108      * Iterate over path components in `left'.
0109      */
0110     while (left_len != 0) {
0111         /*
0112          * Extract the next path component and adjust `left'
0113          * and its length.
0114          */
0115         p = strchr(left, '/');
0116         s = p ? p : left + left_len;
0117         if (s - left >= sizeof(next_token)) {
0118             if (m)
0119                 free(resolved);
0120             errno = ENAMETOOLONG;
0121             return (NULL);
0122         }
0123         memcpy(next_token, left, s - left);
0124         next_token[s - left] = '\0';
0125         left_len -= s - left;
0126         if (p != NULL)
0127             memmove(left, s + 1, left_len + 1);
0128         if (resolved[resolved_len - 1] != '/') {
0129             if (resolved_len + 1 >= PATH_MAX) {
0130                 if (m)
0131                     free(resolved);
0132                 errno = ENAMETOOLONG;
0133                 return (NULL);
0134             }
0135             resolved[resolved_len++] = '/';
0136             resolved[resolved_len] = '\0';
0137         }
0138         if (next_token[0] == '\0') {
0139             /*
0140              * Handle consequential slashes.  The path
0141              * before slash shall point to a directory.
0142              *
0143              * Only the trailing slashes are not covered
0144              * by other checks in the loop, but we verify
0145              * the prefix for any (rare) "//" or "/\0"
0146              * occurence to not implement lookahead.
0147              */
0148             if (lstat(resolved, &sb) != 0) {
0149                 if (m)
0150                     free(resolved);
0151                 return (NULL);
0152             }
0153             if (!S_ISDIR(sb.st_mode)) {
0154                 if (m)
0155                     free(resolved);
0156                 errno = ENOTDIR;
0157                 return (NULL);
0158             }
0159             continue;
0160         }
0161         else if (strcmp(next_token, ".") == 0)
0162             continue;
0163         else if (strcmp(next_token, "..") == 0) {
0164             /*
0165              * Strip the last path component except when we have
0166              * single "/"
0167              */
0168             if (resolved_len > 1) {
0169                 resolved[resolved_len - 1] = '\0';
0170                 q = strrchr(resolved, '/') + 1;
0171                 *q = '\0';
0172                 resolved_len = q - resolved;
0173             }
0174             continue;
0175         }
0176 
0177         /*
0178          * Append the next path component and lstat() it.
0179          */
0180         resolved_len = strlcat(resolved, next_token, PATH_MAX);
0181         if (resolved_len >= PATH_MAX) {
0182             if (m)
0183                 free(resolved);
0184             errno = ENAMETOOLONG;
0185             return (NULL);
0186         }
0187         if (lstat(resolved, &sb) != 0) {
0188             if (m)
0189                 free(resolved);
0190             return (NULL);
0191         }
0192         if (S_ISLNK(sb.st_mode)) {
0193             if (symlinks++ > MAXSYMLINKS) {
0194                 if (m)
0195                     free(resolved);
0196                 errno = ELOOP;
0197                 return (NULL);
0198             }
0199             slen = readlink(resolved, symlink, sizeof(symlink) - 1);
0200             if (slen < 0) {
0201                 if (m)
0202                     free(resolved);
0203                 return (NULL);
0204             }
0205             symlink[slen] = '\0';
0206             if (symlink[0] == '/') {
0207                 resolved[1] = 0;
0208                 resolved_len = 1;
0209             } else if (resolved_len > 1) {
0210                 /* Strip the last path component. */
0211                 resolved[resolved_len - 1] = '\0';
0212                 q = strrchr(resolved, '/') + 1;
0213                 *q = '\0';
0214                 resolved_len = q - resolved;
0215             }
0216 
0217             /*
0218              * If there are any path components left, then
0219              * append them to symlink. The result is placed
0220              * in `left'.
0221              */
0222             if (p != NULL) {
0223                 if (symlink[slen - 1] != '/') {
0224                     if (slen + 1 >= sizeof(symlink)) {
0225                         if (m)
0226                             free(resolved);
0227                         errno = ENAMETOOLONG;
0228                         return (NULL);
0229                     }
0230                     symlink[slen] = '/';
0231                     symlink[slen + 1] = 0;
0232                 }
0233                 left_len = strlcat(symlink, left,
0234                     sizeof(symlink));
0235                 if (left_len >= sizeof(left)) {
0236                     if (m)
0237                         free(resolved);
0238                     errno = ENAMETOOLONG;
0239                     return (NULL);
0240                 }
0241             }
0242             left_len = strlcpy(left, symlink, sizeof(left));
0243         }
0244     }
0245 
0246     /*
0247      * Remove trailing slash except when the resolved pathname
0248      * is a single "/".
0249      */
0250     if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
0251         resolved[resolved_len - 1] = '\0';
0252     return (resolved);
0253 }