LCOV - code coverage report
Current view: top level - lib - realpathat.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 43 95 45.3 %
Date: 2023-04-18 16:19:03 Functions: 1 2 50.0 %

          Line data    Source code
       1             : /* -at() version of realpath()
       2             :    Copyright (C) 2012 L. A. F. Pereira
       3             : 
       4             :    Based on: return the canonical absolute name of a given file.
       5             :    Copyright (C) 1996-2002,2004,2005,2006,2008 Free Software Foundation, Inc.
       6             :    This file is part of the GNU C Library.
       7             : 
       8             :    The GNU C Library is free software; you can redistribute it and/or
       9             :    modify it under the terms of the GNU Lesser General Public
      10             :    License as published by the Free Software Foundation; either
      11             :    version 2.1 of the License, or (at your option) any later version.
      12             : 
      13             :    The GNU C Library is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :    Lesser General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU Lesser General Public
      19             :    License along with the GNU C Library; if not, see
      20             :    <http://www.gnu.org/licenses/>.  */
      21             : 
      22             : #define _GNU_SOURCE
      23             : #include <assert.h>
      24             : #include <errno.h>
      25             : #include <fcntl.h>
      26             : #include <limits.h>
      27             : #include <stddef.h>
      28             : #include <stdlib.h>
      29             : #include <string.h>
      30             : #include <string.h>
      31             : #include <sys/param.h>
      32             : #include <sys/stat.h>
      33             : #include <unistd.h>
      34             : 
      35             : #include "lwan-private.h"
      36             : 
      37             : char *
      38          35 : realpathat2(int dirfd, char *dirfdpath, const char *name, char *resolved,
      39             :         struct stat *st)
      40             : {
      41             :     char *rpath, *dest, extra_buf[PATH_MAX];
      42             :     const char *start, *end, *rpath_limit;
      43          35 :     int num_links = 0;
      44             :     ptrdiff_t dirfdlen;
      45             :     char *pathat;
      46             : 
      47          35 :     if (UNLIKELY(name == NULL)) {
      48             :         /* As per Single Unix Specification V2 we must return an error if
      49             :            either parameter is a null pointer.  We extend this to allow
      50             :            the RESOLVED parameter to be NULL in case the we are expected to
      51             :            allocate the room for the return value.  */
      52           0 :         errno = EINVAL;
      53           0 :         return NULL;
      54             :     }
      55             : 
      56             :     /* If any of the additional parameters are null, or if the name to
      57             :        resolve the real path is an absolute path, use the standard
      58             :        realpath() routine. */
      59          35 :     if (UNLIKELY(dirfd < 0 || dirfdpath == NULL || name[0] == '/'))
      60           0 :         return realpath(name, resolved);
      61             : 
      62          35 :     if (name[0] == '\0') {
      63           4 :         if (UNLIKELY(fstat(dirfd, st) < 0))
      64           0 :             return NULL;
      65           4 :         if (LIKELY(!resolved))
      66           0 :             return strdup(dirfdpath);
      67           4 :         return strcpy(resolved, dirfdpath);
      68             :     }
      69             : 
      70          31 :     if (LIKELY(!resolved)) {
      71           0 :         rpath = malloc(PATH_MAX);
      72           0 :         if (UNLIKELY(!rpath))
      73           0 :             return NULL;
      74             :     } else
      75          31 :         rpath = resolved;
      76          31 :     rpath_limit = rpath + PATH_MAX;
      77             : 
      78          31 :     strcpy(rpath, dirfdpath);
      79          31 :     dest = rpath + strlen(rpath);
      80          31 :     dirfdlen = dest - rpath;
      81             : 
      82          59 :     for (start = end = name; *start; start = end) {
      83             :         int n;
      84             : 
      85             :         /* Skip sequence of multiple path-separators.  */
      86          39 :         while (*start == '/')
      87           4 :             ++start;
      88             : 
      89             :         /* Find end of path component.  */
      90         236 :         for (end = start; *end && *end != '/'; ++end)
      91             :             /* Nothing.  */ ;
      92             : 
      93          35 :         if (end - start == 0)
      94           2 :             break;
      95          33 :         else if (end - start == 1 && start[0] == '.')
      96             :             /* nothing */ ;
      97          33 :         else if (end - start == 2 && start[0] == '.' && start[1] == '.') {
      98             :             /* Back up to previous component, ignore if at root already.  */
      99           0 :             if (dest > rpath + 1)
     100           0 :                 while ((--dest)[-1] != '/');
     101             :         } else {
     102             :             size_t new_size;
     103             : 
     104          33 :             if (dest[-1] != '/')
     105           2 :                 *dest++ = '/';
     106             : 
     107          33 :             if (dest + (end - start) >= rpath_limit) {
     108           0 :                 ptrdiff_t dest_offset = dest - rpath;
     109             :                 char *new_rpath;
     110             : 
     111           0 :                 if (UNLIKELY(resolved != NULL)) {
     112           0 :                     errno = ENAMETOOLONG;
     113           0 :                     if (dest > rpath + 1)
     114           0 :                         dest--;
     115           0 :                     *dest = '\0';
     116           0 :                     goto error;
     117             :                 }
     118             : 
     119           0 :                 new_size = (size_t)(rpath_limit - rpath);
     120           0 :                 if (end - start + 1 > PATH_MAX)
     121           0 :                     new_size += (size_t)(end - start + 1);
     122             :                 else
     123           0 :                     new_size += PATH_MAX;
     124           0 :                 new_rpath = (char *) realloc(rpath, new_size);
     125           0 :                 if (UNLIKELY(new_rpath == NULL))
     126           0 :                     goto error;
     127           0 :                 rpath = new_rpath;
     128           0 :                 rpath_limit = rpath + new_size;
     129             : 
     130           0 :                 dest = rpath + dest_offset;
     131             :             }
     132             : 
     133          33 :             dest = mempmove(dest, start, (size_t)(end - start));
     134          33 :             *dest = '\0';
     135             : 
     136          33 :             if ((dirfdlen == 1 && *dirfdpath == '/') ||
     137          33 :                     strncmp(rpath, dirfdpath, (size_t)dirfdlen)) {
     138           0 :                 pathat = rpath;
     139             :             } else {
     140          33 :                 pathat = rpath + dirfdlen;
     141             :             }
     142          33 :             if (UNLIKELY(*pathat == '\0'))
     143           0 :                 pathat = rpath;
     144             : 
     145          33 :             if (UNLIKELY(fstatat(dirfd, pathat, st, AT_SYMLINK_NOFOLLOW) < 0))
     146           5 :                 goto error;
     147             : 
     148          28 :             if (UNLIKELY(S_ISLNK(st->st_mode))) {
     149             :                 char buf[PATH_MAX];
     150             :                 size_t len;
     151             : 
     152           0 :                 if (UNLIKELY(++num_links > MAXSYMLINKS)) {
     153           0 :                     errno = ELOOP;
     154           0 :                     goto error;
     155             :                 }
     156             : 
     157           0 :                 n = (int)readlinkat(dirfd, pathat, buf, PATH_MAX - 1);
     158           0 :                 if (UNLIKELY(n < 0))
     159           0 :                     goto error;
     160           0 :                 buf[n] = '\0';
     161             : 
     162           0 :                 len = strlen(end);
     163           0 :                 if (UNLIKELY((size_t)(PATH_MAX - n) <= len)) {
     164           0 :                     errno = ENAMETOOLONG;
     165           0 :                     goto error;
     166             :                 }
     167             : 
     168             :                 /* Careful here, end may be a pointer into extra_buf... */
     169           0 :                 memmove(&extra_buf[n], end, len + 1);
     170           0 :                 end = memcpy(extra_buf, buf, (size_t)n);
     171             : 
     172           0 :                 if (buf[0] == '/')
     173           0 :                     dest = rpath + 1;    /* It's an absolute symlink */
     174             :                 else
     175             :                     /* Back up to previous component, ignore if at root already: */
     176           0 :                     if (dest > rpath + 1)
     177           0 :                         while ((--dest)[-1] != '/');
     178          28 :             } else if (UNLIKELY(!S_ISDIR(st->st_mode) && *end != '\0')) {
     179           0 :                 errno = ENOTDIR;
     180           0 :                 goto error;
     181             :             }
     182             :         }
     183             :     }
     184             : 
     185          26 :     if (dest > rpath + 1 && dest[-1] == '/')
     186           0 :         --dest;
     187          26 :     *dest = '\0';
     188             : 
     189          26 :     assert(resolved == NULL || resolved == rpath);
     190          26 :     return rpath;
     191             : 
     192           5 :   error:
     193           5 :     assert(resolved == NULL || resolved == rpath);
     194           5 :     if (resolved == NULL)
     195           0 :         free(rpath);
     196           5 :     return NULL;
     197             : }
     198             : 
     199             : char *
     200           0 : realpathat(int dirfd, char *dirfdpath, const char *name, char *resolved)
     201             : {
     202             :     struct stat st;
     203           0 :     return realpathat2(dirfd, dirfdpath, name, resolved, &st);
     204             : }

Generated by: LCOV version 1.15-2-gb9d6727